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 
33 package org.armedbear.lisp;
34 
35 import static org.armedbear.lisp.Lisp.*;
36 
37 import java.nio.ByteBuffer;
38 import 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.
42 public 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 
ComplexVector_IntBuffer(int capacity)57   public ComplexVector_IntBuffer(int capacity) {
58     this(capacity, false);
59   }
60 
ComplexVector_IntBuffer(int capacity, boolean directAllocation)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 
ComplexVector_IntBuffer(int capacity, AbstractArray array, int displacement)72   public ComplexVector_IntBuffer(int capacity, AbstractArray array,
73                                  int displacement) {
74     this(capacity, array, displacement, false);
75   }
76 
ComplexVector_IntBuffer(int capacity, AbstractArray array, int displacement, boolean directAllocation)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
typeOf()87   public LispObject typeOf() {
88     return list(Symbol.VECTOR, UNSIGNED_BYTE_32, Fixnum.getInstance(capacity));
89   }
90 
91   @Override
classOf()92   public LispObject classOf() {
93     return BuiltInClass.VECTOR;
94   }
95 
96   @Override
hasFillPointer()97   public boolean hasFillPointer() {
98     return fillPointer >= 0;
99   }
100 
101   @Override
getFillPointer()102   public int getFillPointer() {
103     return fillPointer;
104   }
105 
106   @Override
setFillPointer(int n)107   public void setFillPointer(int n) {
108     fillPointer = n;
109   }
110 
111   @Override
setFillPointer(LispObject obj)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
isDisplaced()136   public boolean isDisplaced() {
137     return isDisplaced;
138   }
139 
140   @Override
arrayDisplacement()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
getElementType()154   public LispObject getElementType() {
155     return UNSIGNED_BYTE_32;
156   }
157 
isSimpleVector()158   @Override public boolean isSimpleVector() {
159     return false;
160   }
161 
162   @Override
capacity()163   public int capacity() {
164     return capacity;
165   }
166 
167   @Override
length()168   public int length() {
169     return fillPointer >= 0 ? fillPointer : capacity;
170   }
171 
172   @Override
elt(int index)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
AREF(int index)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
aset(int index, LispObject newValue)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
subseq(int start, int end)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
fill(LispObject obj)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
shrink(int n)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
reverse()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
nreverse()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
vectorPushExtend(LispObject element)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
VECTOR_PUSH_EXTEND(LispObject element)326   public LispObject VECTOR_PUSH_EXTEND(LispObject element) {
327     vectorPushExtend(element);
328     return Fixnum.getInstance(fillPointer - 1);
329   }
330 
331   @Override
VECTOR_PUSH_EXTEND(LispObject element, LispObject extension)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 
ensureCapacity(int minCapacity)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         newBuffer.put(elements);
358         elements = newBuffer;
359         capacity = minCapacity;
360       }
361     } else {
362       // Displaced array.
363       Debug.assertTrue(array != null);
364       if (capacity < minCapacity
365           || array.getTotalSize() - displacement < minCapacity) {
366         // Copy array.
367         if (directAllocation) {
368           ByteBuffer b = ByteBuffer.allocateDirect(minCapacity * 4);
369           elements = b.asIntBuffer();
370         } else {
371           elements = IntBuffer.allocate(minCapacity);
372         }
373         final int limit
374           = Math.min(capacity, array.getTotalSize() - displacement);
375         for (int i = 0; i < limit; i++) {
376           elements.put(i, coerceToJavaUnsignedInt(AREF(displacement + i)));
377         }
378         capacity = minCapacity;
379         array = null;
380         displacement = 0;
381         isDisplaced = false;
382       }
383     }
384   }
385 
386   @Override
adjustArray(int newCapacity, LispObject initialElement, LispObject initialContents)387   public AbstractVector adjustArray(int newCapacity,
388                                     LispObject initialElement,
389                                     LispObject initialContents) {
390     if (initialContents != null) {
391       // "If INITIAL-CONTENTS is supplied, it is treated as for MAKE-
392       // ARRAY. In this case none of the original contents of array
393       // appears in the resulting array."
394       IntBuffer newElements = null;
395       if (directAllocation) {
396         ByteBuffer b = ByteBuffer.allocateDirect(newCapacity * 4);
397         newElements = b.asIntBuffer();
398       } else {
399         newElements = IntBuffer.allocate(newCapacity);
400       }
401       if (initialContents.listp()) {
402         LispObject list = initialContents;
403         for (int i = 0; i < newCapacity; i++) {
404           newElements.put(i, coerceToJavaUnsignedInt(list.car()));
405           list = list.cdr();
406         }
407       } else if (initialContents.vectorp()) {
408         for (int i = 0; i < newCapacity; i++) {
409           newElements.put(i, coerceToJavaUnsignedInt(initialContents.elt(i)));
410 
411         }
412       } else {
413         type_error(initialContents, Symbol.SEQUENCE);
414       }
415       elements = newElements;
416     } else {
417       if (elements == null) {
418         // Displaced array. Copy existing elements.
419         if (directAllocation) {
420           ByteBuffer b = ByteBuffer.allocateDirect(newCapacity * 4);
421           elements = b.asIntBuffer();
422         } else {
423           elements = IntBuffer.allocate(newCapacity);
424         }
425         final int limit = Math.min(capacity, newCapacity);
426         for (int i = 0; i < limit; i++) {
427           elements.put(i,(int)(array.AREF(displacement + i).longValue() & 0xffffffffL));
428         }
429       } else if (capacity != newCapacity) {
430         IntBuffer newElements = null;
431         if (directAllocation) {
432           ByteBuffer b = ByteBuffer.allocateDirect(newCapacity * 4);
433           newElements = b.asIntBuffer();
434         } else {
435           newElements = IntBuffer.allocate(newCapacity);
436         }
437         newElements.put(elements.array(),
438                         0, Math.min(capacity, newCapacity));
439         elements = newElements;
440       }
441       // Initialize new elements (if aapplicable).
442       if (initialElement != null) {
443         for (int i = capacity; i < newCapacity; i++) {
444           elements.put(i, coerceToJavaUnsignedInt(initialElement));
445         }
446       }
447     }
448     capacity = newCapacity;
449     array = null;
450     displacement = 0;
451     isDisplaced = false;
452     return this;
453   }
454 
455   @Override
adjustArray(int newCapacity, AbstractArray displacedTo, int displacement)456     public AbstractVector adjustArray(int newCapacity,
457                                       AbstractArray displacedTo,
458                                       int displacement) {
459     capacity = newCapacity;
460     array = displacedTo;
461     this.displacement = displacement;
462     elements = null;
463     isDisplaced = true;
464     return this;
465   }
466 }
467