/iolists.d

http://github.com/baryluk/cords · D · 250 lines · 213 code · 26 blank · 11 comment · 31 complexity · d4b98459d7102969bd7e829c1ed7930c MD5 · raw file

  1. module iolists;
  2. /** Immutable list based implementation, simple and fast for small strings, or for append/prepend only
  3. * Sometime calles IoList
  4. *
  5. * Actually this is just binary tree, without rebalancing
  6. *
  7. * Few methods which is known to be slow (like opIndexAssign) are removed, because if you really need such functionality,
  8. * you should use another structure. Other mutating methods and slicing (which are also slow), are there for some usages.
  9. */
  10. final class cord_l2(T) {
  11. final class node {
  12. T[] v;
  13. node next;
  14. this() {
  15. v = null;
  16. }
  17. this(in T[] s) {
  18. v = s;
  19. }
  20. this(in T[] s, node n) {
  21. v = s;
  22. next = n;
  23. }
  24. }
  25. node* head;
  26. this() {
  27. first = last = new node(); // guard
  28. }
  29. this(in T[] s) {
  30. first = last = new node(s);
  31. }
  32. // inplace
  33. void opCatAssign(in T[] s) {
  34. last = (last.next = new node(s));
  35. }
  36. cord_l2!(T) opCat(in T[] s)
  37. out (res) {
  38. assert(res.length == this.length + s.length);
  39. }
  40. body {
  41. auto r = this.clone();
  42. r ~= s;
  43. return r;
  44. }
  45. cord_l2!(T) opCat_r(in T[] s)
  46. out (res) {
  47. assert(res.length == this.length + s.length);
  48. }
  49. body {
  50. auto r = this.clone();
  51. r.prepend(s);
  52. return r;
  53. }
  54. // inplace
  55. void prepend(in T[] s) {
  56. first = new node(s, first);
  57. }
  58. int opApply(int delegate(inout T[] chunk) dg) {
  59. int ret;
  60. node current = first;
  61. while (current) {
  62. auto s = cast(T[])current.v;
  63. if ((ret = dg(s)) != 0) {
  64. break;
  65. }
  66. current = current.next;
  67. }
  68. return ret;
  69. }
  70. size_t length() {
  71. size_t l = 0;
  72. foreach (chunk; this) {
  73. l += chunk.length;
  74. }
  75. return l;
  76. }
  77. override invariant(T)[] toString()
  78. out (res) {
  79. assert(res.length == this.length);
  80. }
  81. body {
  82. T[] ret;
  83. ret.length = this.length;
  84. size_t i = 0;
  85. foreach (chunk; this) {
  86. ret[i .. i+chunk.length] = chunk;
  87. i += chunk.length;
  88. }
  89. return cast(invariant)ret;
  90. }
  91. T opIndex(size_t i) {
  92. foreach (chunk; this) {
  93. if (i < chunk.length) {
  94. return chunk[i];
  95. }
  96. i -= chunk.length;
  97. }
  98. throw new Exception("CordBoundsException");
  99. }
  100. cord_l2!(T) clone()
  101. out (res) {
  102. assert(res.length == this.length);
  103. }
  104. body {
  105. auto r = new cord_l2!(T)();
  106. foreach (chunk; this) {
  107. r ~= chunk;
  108. }
  109. return r;
  110. }
  111. cord_l2!(T) opSlice(size_t j1, size_t j2)
  112. in {
  113. assert(j1 <= j2);
  114. assert(j2 <= this.length);
  115. }
  116. out (res) {
  117. assert(res.length == j2-j1);
  118. assert(res.toString() == (this.toString())[j1..j2]);
  119. }
  120. body {
  121. auto r = new cord_l2!(T)();
  122. size_t i = 0;
  123. foreach (chunk; this) {
  124. auto i_end = i+chunk.length;
  125. if (i_end <= j1) {
  126. ;
  127. } else if (i > j2) {
  128. break;
  129. } else if (i <= j1) {
  130. r ~= chunk[j1-i .. $];
  131. } else if (i_end > j2) {
  132. r ~= chunk[0 .. j2-i];
  133. break;
  134. } else {
  135. r ~= chunk;
  136. }
  137. i = i_end;
  138. }
  139. return r;
  140. }
  141. cord_l2!(T) opCat(cord_l2!(T) b)
  142. out (res) {
  143. assert(res.length == this.length + b.length);
  144. }
  145. body {
  146. auto r = this.clone();
  147. foreach (chunk; b) {
  148. r ~= chunk;
  149. }
  150. return r;
  151. }
  152. // inplace
  153. void opCatAssign(cord_l2!(T) b) {
  154. foreach (chunk; b) {
  155. this ~= chunk;
  156. }
  157. }
  158. cord_l2!(T) change(size_t j1, size_t j2, cord_l2!(T) b)
  159. in {
  160. assert(j1 <= j2);
  161. assert(j2 <= this.length);
  162. }
  163. out (res) {
  164. assert(res.length == this.length - (j2-j1) + b.length);
  165. }
  166. body {
  167. auto r = this[0..j1];
  168. r ~= b;
  169. r ~= this[j2..this.length];
  170. return r;
  171. }
  172. cord_l2!(T) change(size_t j1, size_t j2, in T[] b)
  173. in {
  174. assert(j1 <= j2);
  175. assert(j2 <= this.length);
  176. }
  177. out (res) {
  178. assert(res.length == this.length - (j2-j1) + b.length);
  179. }
  180. body {
  181. auto r = this[0..j1];
  182. r ~= b;
  183. r ~= this[j2..this.length];
  184. return r;
  185. }
  186. cord_l2!(T) remove(size_t j1, size_t j2)
  187. in {
  188. assert(j1 <= j2);
  189. assert(j2 <= this.length);
  190. }
  191. out (res) {
  192. assert(res.length == this.length - (j2-j1));
  193. }
  194. body {
  195. auto r = this[0..j1];
  196. r ~= this[j2..this.length];
  197. return r;
  198. }
  199. }
  200. alias cord_l2!(char) cord;
  201. unittest {
  202. auto r = new cord("a");
  203. r ~= "xx";
  204. assert(r.length == 3);
  205. r ~= "yyy";
  206. assert(r.length == 6);
  207. r ~= "zzz";
  208. assert(r.length == 9);
  209. assert(r.toString() == "axxyyyzzz");
  210. r.prepend("aaxa");
  211. r ~= "zazz";
  212. r ~= "zaadz";
  213. r ~= "agnz";
  214. r ~= "cbzz";
  215. r ~= "zasdz";
  216. auto r0 = r[5..26];
  217. assert(r0.length == 21);
  218. for (int i = 0; i < r0.length; i++) {
  219. assert(r0[i] == r[5+i]);
  220. }
  221. auto x = new cord("ala") ~ new cord(" ma ") ~ new cord("kota");
  222. assert(x.length == 11);
  223. assert(x.toString() == "ala ma kota");
  224. }