StableManager.java 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. // Copyright (c) 2001, 2002, 2003, 2015 Per M.A. Bothner and Brainfood Inc.
  2. // This is free software; for terms and warranty disclaimer see ./COPYING.
  3. package gnu.lists;
  4. /** Implements a stable sequence with sticky positions.
  5. * I.e if you have a position, it gets automatically updated after
  6. * insertions and deletions. */
  7. public class StableManager
  8. {
  9. SimpleVector base;
  10. /** This array maps from the exported ipos values (indexes in the positions
  11. * array) to the ipos of the underlying SimpleVector base.
  12. * The first two elements are reserved for START_POSITION and END_POSITION.
  13. * Unused elements in positions are chained together in a free list
  14. * headed by the 'free' variable. */
  15. protected int[] positions;
  16. /** The head of the free elements in position, if they are chained.
  17. * We keep track of available elements in the positions array in two ways:
  18. * In unchained mode, there is no free list per se. Instead an index i
  19. * is available if positions[i]==FREE_POSITION. This mode makes it
  20. * easy to loop over all positions, ignoring the unused ones.
  21. * In chained mode, there is a free list and if index i is available,
  22. * then positions[i] is the next available index, with -1 if there is none.
  23. * Unchained mode is indicated by free==-2.
  24. * In chained mode, free is the first element in the free list,
  25. * or -1 if the free list is empty.
  26. * The main virtue of this convention is that we don't need a separate
  27. * list or array for the free list. But we should get rid of the
  28. * unchained mode, at least. FIXME.
  29. */
  30. protected int free;
  31. /** An invalid value for an in-use element of positions. */
  32. protected static final int FREE_POSITION = -1 << 1;
  33. /** Put all free elements in positions in a chain starting with free. */
  34. protected void chainFreelist()
  35. {
  36. free = -1;
  37. for (int i = positions.length; --i > END_POSITION; )
  38. {
  39. int pos = positions[i];
  40. if (pos == FREE_POSITION)
  41. {
  42. positions[i] = free;
  43. free = i;
  44. }
  45. }
  46. }
  47. /** Set all free elements in positions to FREE_POSITION. */
  48. protected void unchainFreelist()
  49. {
  50. for (int i = free; i >= 0; )
  51. {
  52. int next = positions[i];
  53. positions[i] = FREE_POSITION;
  54. i = next;
  55. }
  56. free = -2;
  57. }
  58. /** Index in positions for the start position.
  59. * positions[START_POSITION] is always 0. */
  60. static final int START_POSITION = 0;
  61. /** Index in positions for the end position.
  62. * positions[END] is always (size()<<1)+1. */
  63. static final int END_POSITION = 1;
  64. public int startPos () { return START_POSITION; }
  65. public int endPos () { return END_POSITION; }
  66. public StableManager(SimpleVector base)
  67. {
  68. this.base = base;
  69. base.gapReserveGeneric(base.size(), 0); // To force to GAP mode.
  70. positions = new int[16];
  71. positions[START_POSITION] = 0;
  72. positions[END_POSITION] = (base.getBufferLength() << 1) | 1;
  73. free = -1;
  74. for (int i = positions.length; --i > END_POSITION; )
  75. {
  76. positions[i] = free;
  77. free = i;
  78. }
  79. }
  80. protected int allocPositionIndex()
  81. {
  82. if (free == -2)
  83. chainFreelist();
  84. if (free < 0)
  85. {
  86. int oldLength = positions.length;
  87. int[] tmp = new int[2 * oldLength];
  88. System.arraycopy(positions, 0, tmp, 0, oldLength);
  89. for (int i = 2 * oldLength; --i >= oldLength; )
  90. {
  91. tmp[i] = free;
  92. free = i;
  93. }
  94. positions = tmp;
  95. }
  96. int pos = free;
  97. free = positions[free];
  98. return pos;
  99. }
  100. public int createPos (int index, boolean isAfter)
  101. {
  102. if (index == 0 && ! isAfter)
  103. return START_POSITION;
  104. else if (isAfter && index == base.size())
  105. return END_POSITION;
  106. int gapStart = base.getGapStart();
  107. int gapEnd = base.getGapEnd();
  108. if (index > gapStart || (index == gapStart && isAfter))
  109. index += gapEnd - gapStart;
  110. int ipos = allocPositionIndex();
  111. positions[ipos] = (index << 1) | (isAfter ? 1 : 0);
  112. return ipos;
  113. }
  114. protected boolean isAfterPos (int ipos)
  115. {
  116. return (positions[ipos] & 1) != 0;
  117. }
  118. public boolean hasNext(int ipos)
  119. {
  120. int ppos = positions[ipos];
  121. int index = ppos >>> 1;
  122. int gapStart = base.getGapStart();
  123. if (index >= gapStart)
  124. index += base.getGapEnd() - gapStart;
  125. return index < base.getBufferLength();
  126. }
  127. public int nextPos (int ipos)
  128. {
  129. int ppos = positions[ipos];
  130. int index = ppos >>> 1;
  131. int gapStart = base.getGapStart();
  132. int gapEnd = base.getGapEnd();
  133. if (index >= gapStart)
  134. index += gapEnd - gapStart;
  135. if (index >= base.getBufferLength())
  136. {
  137. releasePos(ipos);
  138. return 0;
  139. }
  140. if (ipos == 0)
  141. ipos = createPos(0, true);
  142. positions[ipos] = ppos | 1;
  143. return ipos;
  144. }
  145. public int nextIndex (int ipos)
  146. {
  147. int index = positions[ipos] >>> 1;
  148. int gapStart = base.getGapStart();
  149. int gapEnd = base.getGapEnd();
  150. if (index > gapStart)
  151. index -= gapEnd - gapStart;
  152. return index;
  153. }
  154. public void releasePos(int ipos)
  155. {
  156. if (ipos >= 2)
  157. {
  158. if (free == -2)
  159. chainFreelist();
  160. positions[ipos] = free;
  161. free = ipos;
  162. }
  163. }
  164. public int copyPos (int ipos)
  165. {
  166. if (ipos > END_POSITION)
  167. {
  168. int i = allocPositionIndex();
  169. positions[i] = positions[ipos];
  170. ipos = i;
  171. }
  172. return ipos;
  173. }
  174. /** Adjust gap to 'where', and make sure it is least `needed'
  175. * elements long. */
  176. protected void gapReserve(SimpleVector base, int where, int needed) {
  177. int oldGapEnd = base.getGapEnd();
  178. int oldGapStart = base.getGapStart();
  179. int oldLength = base.getBufferLength();
  180. base.gapReserveGeneric(where, needed);
  181. if (needed > oldGapEnd - oldGapStart) {
  182. int newLength = base.getBufferLength();
  183. if (where == oldGapStart) // Optimization.
  184. adjustPositions(oldGapEnd << 1, (newLength << 1) | 1,
  185. (newLength - oldLength) << 1);
  186. else {
  187. // We do adjustPositions twice which is wasteful but simple.
  188. // Adjust positions as if there were no gap.
  189. adjustPositions(oldGapEnd << 1, (oldLength << 1) | 1, (oldGapStart - oldGapEnd) << 1);
  190. // Adjust positions for new gap.
  191. int gapStart = base.getGapStart();
  192. int gapEnd = base.getGapEnd();
  193. adjustPositions(gapStart << 1, (newLength << 1) | 1, (gapEnd - gapStart) << 1);
  194. }
  195. } else if (where != oldGapStart) {
  196. int delta = where - oldGapStart;
  197. int low, high, adjust;
  198. if (delta > 0) {
  199. low = oldGapEnd;
  200. high = low + delta;
  201. adjust = (oldGapStart - low) << 1;
  202. // The position corresponding to the new endGap should be
  203. // adjusted only if it has the isAfter (low-order) bit is clear.
  204. low = low << 1;
  205. high = high << 1;
  206. }
  207. else { // newGapStart < oldGapStart:
  208. // Positions at the newgapEnd should be adjust only if isAfter.
  209. low = (where << 1) + 1;
  210. high = (oldGapStart << 1) + 1;
  211. adjust = (oldGapEnd - oldGapStart) << 1;
  212. }
  213. adjustPositions(low, high, adjust);
  214. }
  215. }
  216. /** Add a delta to all positions elements that point into a given range.
  217. * Assume {@code x==positions[i]}, then if
  218. * {@code (unsigned)x>=(unsigned)low && (unsigned)x <= (unsigned)high},
  219. * then add {@code delta} to {@code positions[i]}.
  220. * Using unsigned comparisons allows us to compare ipos values,
  221. * which include both the index and the isAfter low-order bit. */
  222. protected void adjustPositions(int low, int high, int delta)
  223. {
  224. // Swing has positions in an array ordered by offset.
  225. // That means it can use binary search to find only those
  226. // positions that need to adjust, rather than check all the positions
  227. // (including the 'free' ones). FIXME.
  228. if (free >= -1)
  229. unchainFreelist();
  230. // Invert the high-order bit, because:
  231. // (unsigned) X > (unsigned) Y
  232. // iff (int) (X^0x80000000) > (int) (Y^0x80000000)
  233. low = low ^ 0x80000000;
  234. high = high ^ 0x80000000;
  235. for (int i = positions.length; --i > START_POSITION; )
  236. {
  237. int pos = positions[i];
  238. if (pos != FREE_POSITION)
  239. {
  240. int index = pos ^ 0x80000000;
  241. if (index >= low && index <= high)
  242. positions[i] = pos + delta;
  243. }
  244. }
  245. }
  246. /*
  247. protected int addPos(int ipos, E value)
  248. {
  249. int ppos = positions[ipos];
  250. int index = ppos >>> 1;
  251. if (index >= gapStart)
  252. index += gapEnd - gapStart;
  253. // Force positions[ipos] to have the isAfter property.
  254. if ((ppos & 1) == 0)
  255. {
  256. if (ipos == 0)
  257. ipos = createPos(0, true);
  258. else
  259. positions[ipos] = ppos | 1;
  260. }
  261. add(index, value);
  262. return ipos;
  263. }
  264. */
  265. protected void removePosRange(int ipos0, int ipos1)
  266. {
  267. throw new Error();
  268. /*
  269. super.removePosRange(positions[ipos0], positions[ipos1]);
  270. // adjust positions in gap
  271. int low = gapStart;
  272. int high = gapEnd;
  273. if (free >= -1)
  274. unchainFreelist();
  275. for (int i = positions.length; --i > START_POSITION; )
  276. {
  277. int pos = positions[i];
  278. if (pos != FREE_POSITION)
  279. {
  280. int index = pos >> 1;
  281. boolean isAfter = (pos & 1) != 0;
  282. if (isAfter)
  283. {
  284. if (index >= low && index < high)
  285. positions[i] = (gapEnd << 1) | 1;
  286. }
  287. else
  288. {
  289. if (index > low && index <= high)
  290. positions[i] = (gapStart << 1);
  291. }
  292. }
  293. }
  294. */
  295. }
  296. /*
  297. public Object remove(int index)
  298. {
  299. // FIXME
  300. }
  301. */
  302. public void consumePosRange (int iposStart, int iposEnd, Consumer out)
  303. {
  304. throw new Error();
  305. //super.consumePosRange(positions[iposStart], positions[iposEnd], out);
  306. }
  307. /* DEBUGGING
  308. void checkInvariants()
  309. {
  310. boolean wasChained = free >= -1;
  311. if (wasChained)
  312. unchainFreelist();
  313. int gapStart2 = base.getGapStart() << 1;
  314. int gapEnd2 = base.getGapEnd() << 1;
  315. for (int i = positions.length; --i >= 0; ) {
  316. int pos = positions[i];
  317. if (pos == FREE_POSITION)
  318. continue;
  319. if (pos < 0 || nextIndex(i) > base.size()
  320. || (pos > gapStart2 && pos <= gapEnd2)) {
  321. throw new Error("bad position#"+i+": "+pos+" gapStart:"+base.getGapStart()+" gapEnd:"+base.getGapEnd()+" bufLen:"+base.getBufferLength());
  322. }
  323. }
  324. if (wasChained)
  325. chainFreelist();
  326. }
  327. */
  328. }