source: trunk/abcl/src/org/armedbear/lisp/ComplexVector_IntBuffer.java

Last change on this file was 15481, checked in by Mark Evenson, 3 years ago

arrays: fix fail to reset position after relative access

The position of all buffers should always be zero. TODO how to
unintrusively make this assertion?

We fix new types specializing (or (unsigned-byte 8) (unsigned-byte
32)) in BasicVector_ByteBuffer, ComplexArray_ByteBuffer,
ComplexVector_ByteBuffer, and ComplexVector_IntBuffer for relative
accesses which always advance the position of the
underlying java.nio.Buffer subclass.

Fixes <https://github.com/armedbear/abcl/issues/345>.

File size: 13.4 KB
Line 
1/*
2 * ComplexVector_IntBuffer.java
3 *
4 * Copyright (C) 2020 @easye
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19 *
20 * As a special exception, the copyright holders of this library give you
21 * permission to link this library with independent modules to produce an
22 * executable, regardless of the license terms of these independent
23 * modules, and to copy and distribute the resulting executable under
24 * terms of your choice, provided that you also meet, for each linked
25 * independent module, the terms and conditions of the license of that
26 * module.  An independent module is a module which is not derived from
27 * or based on this library.  If you modify this library, you may extend
28 * this exception to your version of the library, but you are not
29 * obligated to do so.  If you do not wish to do so, delete this
30 * exception statement from your version.
31 */
32
33package org.armedbear.lisp;
34
35import static org.armedbear.lisp.Lisp.*;
36
37import java.nio.ByteBuffer;
38import java.nio.IntBuffer;
39
40// A specialized vector of element type (UNSIGNED-BYTE 32) that is displaced to
41// another array, has a fill pointer, and/or is expressly adjustable.
42public final class ComplexVector_IntBuffer
43  extends AbstractVector
44{
45  private int capacity;
46  private int fillPointer = -1; // -1 indicates no fill pointer.
47  private boolean isDisplaced;
48
49  // For non-displaced arrays.
50  private IntBuffer elements;
51  private boolean directAllocation;
52
53  // For displaced arrays.
54  private AbstractArray array;
55  private int displacement;
56
57  public ComplexVector_IntBuffer(int capacity) {
58    this(capacity, false);
59  }
60
61  public ComplexVector_IntBuffer(int capacity, boolean directAllocation) {
62    this.capacity = capacity;
63    this.directAllocation = directAllocation;
64    if (directAllocation) {
65      ByteBuffer b = ByteBuffer.allocateDirect(capacity * 4);
66      elements = b.asIntBuffer();
67    } else {
68      elements = IntBuffer.allocate(capacity);
69    }
70  }
71
72  public ComplexVector_IntBuffer(int capacity, AbstractArray array,
73                                 int displacement) {
74    this(capacity, array, displacement, false);
75  }
76
77  public ComplexVector_IntBuffer(int capacity, AbstractArray array,
78                                      int displacement, boolean directAllocation) {
79    this.capacity = capacity;
80    this.array = array;
81    this.displacement = displacement;
82    this.directAllocation = directAllocation;
83    isDisplaced = true;
84  }
85
86  @Override
87  public LispObject typeOf() {
88    return list(Symbol.VECTOR, UNSIGNED_BYTE_32, Fixnum.getInstance(capacity));
89  }
90
91  @Override
92  public LispObject classOf() {
93    return BuiltInClass.VECTOR;
94  }
95
96  @Override
97  public boolean hasFillPointer() {
98    return fillPointer >= 0;
99  }
100
101  @Override
102  public int getFillPointer() {
103    return fillPointer;
104  }
105
106  @Override
107  public void setFillPointer(int n) {
108    fillPointer = n;
109  }
110
111  @Override
112  public void setFillPointer(LispObject obj) {
113    if (obj == T) {
114      fillPointer = capacity();
115    } else {
116      int n = Fixnum.getValue(obj);
117      if (n > capacity()) {
118        StringBuffer sb = new StringBuffer("The new fill pointer (");
119        sb.append(n);
120        sb.append(") exceeds the capacity of the vector (");
121        sb.append(capacity());
122        sb.append(").");
123        error(new LispError(sb.toString()));
124      } else if (n < 0) {
125        StringBuffer sb = new StringBuffer("The new fill pointer (");
126        sb.append(n);
127        sb.append(") is negative.");
128        error(new LispError(sb.toString()));
129      } else {
130                fillPointer = n;
131      }
132    }
133  }
134
135  @Override
136  public boolean isDisplaced() {
137    return isDisplaced;
138  }
139
140  @Override
141  public LispObject arrayDisplacement() {
142    LispObject value1, value2;
143    if (array != null) {
144      value1 = array;
145      value2 = Fixnum.getInstance(displacement);
146    } else {
147      value1 = NIL;
148      value2 = Fixnum.ZERO;
149    }
150    return LispThread.currentThread().setValues(value1, value2);
151  }
152
153  @Override
154  public LispObject getElementType() {
155    return UNSIGNED_BYTE_32;
156  }
157
158  @Override public boolean isSimpleVector() {
159    return false;
160  }
161
162  @Override
163  public int capacity() {
164    return capacity;
165  }
166
167  @Override
168  public int length() {
169    return fillPointer >= 0 ? fillPointer : capacity;
170  }
171
172  @Override
173  public LispObject elt(int index) {
174    final int limit = length();
175    if (index < 0 || index >= limit)
176      badIndex(index, limit);
177    return AREF(index);
178  }
179
180  // Ignores fill pointer.
181  @Override
182  public LispObject AREF(int index) {
183    if (elements != null) {
184      try {
185        return number(((long)elements.get(index)) & 0xffffffffL);
186      } catch (IndexOutOfBoundsException e) {
187        badIndex(index, ((java.nio.Buffer)elements).limit());
188        return NIL; // Not reached.
189      }
190    } else {
191      // Displaced array.
192      if (index < 0 || index >= capacity) {
193        badIndex(index, capacity);
194      }
195      return array.AREF(index + displacement);
196    }
197  }
198
199  @Override
200  public void aset(int index, LispObject newValue) {
201    if (newValue.isLessThan(Fixnum.ZERO)
202        || newValue.isGreaterThan(UNSIGNED_BYTE_32_MAX_VALUE)) {
203      type_error(newValue, UNSIGNED_BYTE_32);
204    }
205    if (elements != null) {
206      try {
207        elements.put(index, (int)(newValue.longValue() & 0xffffffffL));
208      } catch (IndexOutOfBoundsException e) {
209        badIndex(index, ((java.nio.Buffer)elements).limit());
210      }
211    } else {
212      // Displaced array.
213      if (index < 0 || index >= capacity) {
214        badIndex(index, capacity);
215      } else {
216        array.aset(index + displacement, newValue);
217      }
218    }
219  }
220
221  @Override
222  public LispObject subseq(int start, int end) {
223    SimpleVector v = new SimpleVector(end - start);
224    int i = start, j = 0;
225    try {
226      while (i < end) {
227        v.aset(j++, AREF(i++));
228      }
229      return v;
230    } catch (IndexOutOfBoundsException e) {
231      return error(new TypeError("Array index out of bounds: " + i + "."));
232    }
233  }
234
235  @Override
236  public void fill(LispObject obj) {
237    if (!(obj instanceof LispInteger)) {
238      type_error(obj, Symbol.INTEGER);
239      // Not reached.
240      return;
241    }
242    if (obj.isLessThan(Fixnum.ZERO) || obj.isGreaterThan(UNSIGNED_BYTE_32_MAX_VALUE)) {
243      type_error(obj, UNSIGNED_BYTE_32);
244    }
245    for (int i = capacity; i-- > 0;) {
246      elements.put(i, coerceToJavaUnsignedInt(obj));
247    }
248  }
249
250  @Override
251  public void shrink(int n) {
252    // One cannot shrink the underlying ByteBuffer physically, so
253    // use the limit marker to denote the length
254    if (n < length()) {
255      ((java.nio.Buffer)elements).limit(n);
256      this.capacity = n;
257      return;
258    }
259    if (n == ((java.nio.Buffer)elements).limit()) {
260      return;
261    }
262    error(new LispError());
263  }
264
265  @Override
266  public LispObject reverse() {
267    int length = length();
268    SimpleVector result = new SimpleVector(length);
269    int i, j;
270    for (i = 0, j = length - 1; i < length; i++, j--) {
271      result.aset(i, AREF(j));
272    }
273    return result;
274  }
275
276  @Override
277  public LispObject nreverse() {
278    if (elements != null) {
279      int i = 0;
280      int j = length() - 1;
281      while (i < j) {
282        int temp = elements.get(i);
283        elements.put(i, elements.get(j));
284        elements.put(j, temp);
285        ++i;
286        --j;
287      }
288    } else {
289      // Displaced array.
290      int length = length();
291      IntBuffer data = null;
292      if (directAllocation) {
293        ByteBuffer b = ByteBuffer.allocateDirect(length * 4);
294        data = b.asIntBuffer();
295      } else {
296        data = IntBuffer.allocate(length);
297      }
298      int i, j;
299      for (i = 0, j = length - 1; i < length; i++, j--) {
300        data.put(i, coerceToJavaUnsignedInt(AREF(j)));
301      }
302      elements = data;
303      capacity = length;
304      array = null;
305      displacement = 0;
306      isDisplaced = false;
307      fillPointer = -1;
308    }
309    return this;
310  }
311
312  @Override
313  public void vectorPushExtend(LispObject element) {
314    if (fillPointer < 0) {
315      noFillPointer();
316    }
317    if (fillPointer >= capacity) {
318      // Need to extend vector.
319      ensureCapacity(capacity * 2 + 1);
320    }
321    aset(fillPointer, element);
322    ++fillPointer;
323  }
324
325  @Override
326  public LispObject VECTOR_PUSH_EXTEND(LispObject element) {
327    vectorPushExtend(element);
328    return Fixnum.getInstance(fillPointer - 1);
329  }
330
331  @Override
332  public LispObject VECTOR_PUSH_EXTEND(LispObject element,
333                                       LispObject extension) {
334    int ext = Fixnum.getValue(extension);
335    if (fillPointer < 0) {
336      noFillPointer();
337    }
338    if (fillPointer >= capacity) {
339      // Need to extend vector.
340      ext = Math.max(ext, capacity + 1);
341      ensureCapacity(capacity + ext);
342    }
343    aset(fillPointer, element);
344    return Fixnum.getInstance(fillPointer++);
345  }
346
347  private final void ensureCapacity(int minCapacity) {
348    if (elements != null) {
349      if (capacity < minCapacity) {
350        IntBuffer newBuffer = null;
351        if (directAllocation) {
352          ByteBuffer b = ByteBuffer.allocateDirect(minCapacity * 4);
353          newBuffer = b.asIntBuffer();
354        } else {
355          newBuffer = IntBuffer.allocate(minCapacity);
356        }
357        elements.position(0);
358        newBuffer.put(elements);
359        newBuffer.position(0);
360        elements = newBuffer;
361        capacity = minCapacity;
362      }
363    } else {
364      // Displaced array.
365      Debug.assertTrue(array != null);
366      if (capacity < minCapacity
367          || array.getTotalSize() - displacement < minCapacity) {
368        // Copy array.
369        if (directAllocation) {
370          ByteBuffer b = ByteBuffer.allocateDirect(minCapacity * 4);
371          elements = b.asIntBuffer();
372        } else {
373          elements = IntBuffer.allocate(minCapacity);
374        }
375        final int limit
376          = Math.min(capacity, array.getTotalSize() - displacement);
377        for (int i = 0; i < limit; i++) {
378          elements.put(i, coerceToJavaUnsignedInt(AREF(displacement + i)));
379        }
380        capacity = minCapacity;
381        array = null;
382        displacement = 0;
383        isDisplaced = false;
384      }
385    }
386  }
387
388  @Override
389  public AbstractVector adjustArray(int newCapacity,
390                                    LispObject initialElement,
391                                    LispObject initialContents) {
392    if (initialContents != null) {
393      // "If INITIAL-CONTENTS is supplied, it is treated as for MAKE-
394      // ARRAY. In this case none of the original contents of array
395      // appears in the resulting array."
396      IntBuffer newElements = null;
397      if (directAllocation) {
398        ByteBuffer b = ByteBuffer.allocateDirect(newCapacity * 4);
399        newElements = b.asIntBuffer();
400      } else {
401        newElements = IntBuffer.allocate(newCapacity);
402      }
403      if (initialContents.listp()) {
404        LispObject list = initialContents;
405        for (int i = 0; i < newCapacity; i++) {
406          newElements.put(i, coerceToJavaUnsignedInt(list.car()));
407          list = list.cdr();
408        }
409      } else if (initialContents.vectorp()) {
410        for (int i = 0; i < newCapacity; i++) {
411          newElements.put(i, coerceToJavaUnsignedInt(initialContents.elt(i)));
412
413        }
414      } else {
415        type_error(initialContents, Symbol.SEQUENCE);
416      }
417      elements = newElements;
418    } else {
419      if (elements == null) {
420        // Displaced array. Copy existing elements.
421        if (directAllocation) {
422          ByteBuffer b = ByteBuffer.allocateDirect(newCapacity * 4);
423          elements = b.asIntBuffer();
424        } else {
425          elements = IntBuffer.allocate(newCapacity);
426        }
427        final int limit = Math.min(capacity, newCapacity);
428        for (int i = 0; i < limit; i++) {
429          elements.put(i,(int)(array.AREF(displacement + i).longValue() & 0xffffffffL));
430        }
431      } else if (capacity != newCapacity) {
432        IntBuffer newElements = null;
433        if (directAllocation) {
434          ByteBuffer b = ByteBuffer.allocateDirect(newCapacity * 4);
435          newElements = b.asIntBuffer();
436        } else {
437          newElements = IntBuffer.allocate(newCapacity);
438        }
439        newElements.put(elements.array(),
440                        0, Math.min(capacity, newCapacity));
441        newElements.position(0);
442        elements = newElements;
443      }
444      // Initialize new elements (if aapplicable).
445      if (initialElement != null) {
446        for (int i = capacity; i < newCapacity; i++) {
447          elements.put(i, coerceToJavaUnsignedInt(initialElement));
448        }
449      }
450    }
451    capacity = newCapacity;
452    array = null;
453    displacement = 0;
454    isDisplaced = false;
455    return this;
456  }
457
458  @Override
459    public AbstractVector adjustArray(int newCapacity,
460                                      AbstractArray displacedTo,
461                                      int displacement) {
462    capacity = newCapacity;
463    array = displacedTo;
464    this.displacement = displacement;
465    elements = null;
466    isDisplaced = true;
467    return this;
468  }
469}
Note: See TracBrowser for help on using the repository browser.