1 /*
2  * ComplexVector_ByteBuffer.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 
39 // A specialized vector of element type (UNSIGNED-BYTE 8) that is displaced to
40 // another array, has a fill pointer, and/or is expressly adjustable.
41 public final class ComplexVector_ByteBuffer extends AbstractVector
42 {
43   private int capacity;
44   private int fillPointer = -1; // -1 indicates no fill pointer.
45   private boolean isDisplaced;
46 
47   // For non-displaced arrays.
48   private ByteBuffer elements;
49   private boolean directAllocation;
50 
51   // For displaced arrays.
52   private AbstractArray array;
53   private int displacement;
54 
ComplexVector_ByteBuffer(int capacity)55   public ComplexVector_ByteBuffer(int capacity) {
56     this(capacity, false);
57   }
58 
ComplexVector_ByteBuffer(int capacity, boolean directAllocation)59   public ComplexVector_ByteBuffer(int capacity, boolean directAllocation) {
60     this.capacity = capacity;
61     this.directAllocation = directAllocation;
62     if (directAllocation) {
63       elements = ByteBuffer.allocateDirect(capacity);
64     } else {
65       elements = ByteBuffer.allocate(capacity);
66     }
67   }
68 
ComplexVector_ByteBuffer(int capacity, AbstractArray array, int displacement)69   public ComplexVector_ByteBuffer(int capacity, AbstractArray array,
70                                   int displacement) {
71     this(capacity, array, displacement, false);
72   }
73 
ComplexVector_ByteBuffer(int capacity, AbstractArray array, int displacement, boolean directAllocation)74   public ComplexVector_ByteBuffer(int capacity, AbstractArray array,
75                                   int displacement,
76                                   boolean directAllocation) {
77     this.capacity = capacity;
78     this.array = array;
79     this.displacement = displacement;
80     this.directAllocation = directAllocation;
81     isDisplaced = true;
82   }
83 
84   @Override
typeOf()85   public LispObject typeOf() {
86     return list(Symbol.VECTOR, UNSIGNED_BYTE_8, Fixnum.getInstance(capacity));
87   }
88 
89   @Override
classOf()90   public LispObject classOf() {
91     return BuiltInClass.VECTOR;
92   }
93 
94   @Override
hasFillPointer()95   public boolean hasFillPointer() {
96     return fillPointer >= 0;
97   }
98 
99   @Override
getFillPointer()100   public int getFillPointer()
101   {
102     return fillPointer;
103   }
104 
105   @Override
setFillPointer(int n)106   public void setFillPointer(int n)
107   {
108     fillPointer = n;
109   }
110 
111   @Override
setFillPointer(LispObject obj)112   public void setFillPointer(LispObject obj)
113   {
114     if (obj == T)
115       fillPointer = capacity();
116     else {
117       int n = Fixnum.getValue(obj);
118       if (n > capacity()) {
119         StringBuffer sb = new StringBuffer("The new fill pointer (");
120         sb.append(n);
121         sb.append(") exceeds the capacity of the vector (");
122         sb.append(capacity());
123         sb.append(").");
124         error(new LispError(sb.toString()));
125       } else if (n < 0) {
126         StringBuffer sb = new StringBuffer("The new fill pointer (");
127         sb.append(n);
128         sb.append(") is negative.");
129         error(new LispError(sb.toString()));
130       } else
131         fillPointer = n;
132     }
133   }
134 
135   @Override
isDisplaced()136   public boolean isDisplaced()
137   {
138     return isDisplaced;
139   }
140 
141   @Override
arrayDisplacement()142   public LispObject arrayDisplacement()
143   {
144     LispObject value1, value2;
145     if (array != null) {
146       value1 = array;
147       value2 = Fixnum.getInstance(displacement);
148     } else {
149       value1 = NIL;
150       value2 = Fixnum.ZERO;
151     }
152     return LispThread.currentThread().setValues(value1, value2);
153   }
154 
155   @Override
getElementType()156   public LispObject getElementType()
157   {
158     return UNSIGNED_BYTE_8;
159   }
160 
161   @Override
isSimpleVector()162   public boolean isSimpleVector()
163   {
164     return false;
165   }
166 
167   @Override
capacity()168   public int capacity()
169   {
170     return capacity;
171   }
172 
173   @Override
length()174   public int length() {
175     return fillPointer >= 0 ? fillPointer : capacity;
176   }
177 
178   @Override
elt(int index)179   public LispObject elt(int index)
180   {
181     final int limit = length();
182     if (index < 0 || index >= limit)
183       badIndex(index, limit);
184     return AREF(index);
185   }
186 
187   // Ignores fill pointer.
188   @Override
AREF(int index)189   public LispObject AREF(int index) {
190     if (elements != null) {
191       try {
192         return coerceFromJavaByte(elements.get(index));
193       } catch (ArrayIndexOutOfBoundsException e) {
194         badIndex(index, ((java.nio.Buffer)elements).limit());
195         return NIL; // Not reached.
196       }
197     } else {
198       // Displaced array.
199       if (index < 0 || index >= capacity) {
200         badIndex(index, capacity);
201       }
202       return array.AREF(index + displacement);
203     }
204   }
205 
206   @Override
aset(int index, int n)207   public void aset(int index, int n) {
208     if (elements != null) {
209       try {
210         elements.put(index, (byte) n);
211       } catch (IndexOutOfBoundsException e) {
212         badIndex(index, capacity);
213       }
214     } else {
215       // Displaced array.
216       if (index < 0 || index >= capacity) {
217         badIndex(index, capacity);
218       } else {
219         array.aset(index + displacement, n);
220       }
221     }
222   }
223 
224   @Override
aset(int index, LispObject newValue)225   public void aset(int index, LispObject newValue)
226   {
227     if (elements != null) {
228       try {
229         elements.put(index, coerceToJavaByte(newValue));
230       } catch (IndexOutOfBoundsException e) {
231         badIndex(index, ((java.nio.Buffer)elements).limit());
232       }
233     } else {
234       array.aset(index + displacement, newValue);
235     }
236   }
237 
238   @Override
subseq(int start, int end)239   public LispObject subseq(int start, int end) {
240     SimpleVector v = new SimpleVector(end - start);
241     int i = start, j = 0;
242     try {
243       while (i < end)
244         v.aset(j++, AREF(i++));
245       return v;
246     }
247     catch (IndexOutOfBoundsException e) {
248       return error(new TypeError("Array index out of bounds: " + i + "."));
249     }
250   }
251 
252   @Override
fill(LispObject obj)253   public void fill(LispObject obj)
254   {
255     if (!(obj instanceof Fixnum)) {
256       type_error(obj, Symbol.FIXNUM);
257       // Not reached.
258       return;
259     }
260     int n = ((Fixnum) obj).value;
261     if (n < 0 || n > 255) {
262       type_error(obj, UNSIGNED_BYTE_8);
263       // Not reached.
264       return;
265     }
266     for (int i = capacity; i-- > 0;)
267       elements.put(i, (byte) n);
268   }
269 
270   @Override
shrink(int n)271   public void shrink(int n) {
272     // One cannot shrink the underlying ByteBuffer physically, so
273     // use the limit marker to denote the length
274     if (n < length()) {
275       ((java.nio.Buffer)elements).limit(n);
276       this.capacity = n;
277       return;
278     }
279     if (n == ((java.nio.Buffer)elements).limit()) {
280       return;
281     }
282     error(new LispError());
283   }
284 
285   @Override
reverse()286   public LispObject reverse()
287   {
288     int length = length();
289     BasicVector_ByteBuffer result = new BasicVector_ByteBuffer(length, directAllocation);
290     int i, j;
291     for (i = 0, j = length - 1; i < length; i++, j--)
292       result.aset(i, AREF(j));
293     return result;
294   }
295 
296   @Override
nreverse()297   public LispObject nreverse() {
298     if (elements != null) {
299       int i = 0;
300       int j = length() - 1;
301       while (i < j) {
302         byte temp = elements.get(i);
303         elements.put(i, elements.get(j));
304         elements.put(j, temp);
305         ++i;
306         --j;
307       }
308     } else {
309       // Displaced array.
310       int length = length();
311       ByteBuffer data = null;
312       if (directAllocation) {
313         data = ByteBuffer.allocateDirect(length);
314       } else {
315         data = ByteBuffer.allocate(length);
316       }
317       int i, j;
318       for (i = 0, j = length - 1; i < length; i++, j--) {
319         data.put(i, coerceToJavaByte(AREF(j)));
320       }
321       elements = data;
322       capacity = length;
323       array = null;
324       displacement = 0;
325       isDisplaced = false;
326       fillPointer = -1;
327     }
328     return this;
329   }
330 
331   @Override
vectorPushExtend(LispObject element)332   public void vectorPushExtend(LispObject element)
333   {
334     if (fillPointer < 0)
335       noFillPointer();
336     if (fillPointer >= capacity) {
337       // Need to extend vector.
338       ensureCapacity(capacity * 2 + 1);
339     }
340     aset(fillPointer, element);
341     ++fillPointer;
342   }
343 
344   @Override
VECTOR_PUSH_EXTEND(LispObject element)345   public LispObject VECTOR_PUSH_EXTEND(LispObject element)
346 
347   {
348     vectorPushExtend(element);
349     return Fixnum.getInstance(fillPointer - 1);
350   }
351 
352   @Override
VECTOR_PUSH_EXTEND(LispObject element, LispObject extension)353   public LispObject VECTOR_PUSH_EXTEND(LispObject element, LispObject extension)
354 
355   {
356     int ext = Fixnum.getValue(extension);
357     if (fillPointer < 0)
358       noFillPointer();
359     if (fillPointer >= capacity) {
360       // Need to extend vector.
361       ext = Math.max(ext, capacity + 1);
362       ensureCapacity(capacity + ext);
363     }
364     aset(fillPointer, element);
365     return Fixnum.getInstance(fillPointer++);
366   }
367 
ensureCapacity(int minCapacity)368   private final void ensureCapacity(int minCapacity) {
369     if (elements != null) {
370       if (capacity < minCapacity) {
371         ByteBuffer newBuffer = null;
372         if (directAllocation) {
373           newBuffer = ByteBuffer.allocateDirect(minCapacity);
374         } else {
375           newBuffer = ByteBuffer.allocate(minCapacity);
376         }
377         newBuffer.put(elements);
378         elements = newBuffer;
379         capacity = minCapacity;
380       }
381     } else {
382       // Displaced array.
383       Debug.assertTrue(array != null);
384       if (capacity < minCapacity
385           || array.getTotalSize() - displacement < minCapacity) {
386           // Copy array.
387         if (directAllocation) {
388           elements = ByteBuffer.allocateDirect(minCapacity);
389         } else {
390           elements = ByteBuffer.allocate(minCapacity);
391         }
392         final int limit
393           = Math.min(length(), array.getTotalSize() - displacement);
394         for (int i = 0; i < limit; i++) {
395           elements.put(i, coerceToJavaByte(array.AREF(displacement + i)));
396         }
397         capacity = minCapacity;
398         array = null;
399         displacement = 0;
400         isDisplaced = false;
401       }
402     }
403   }
404 
405   @Override
adjustArray(int newCapacity, LispObject initialElement, LispObject initialContents)406   public AbstractVector adjustArray(int newCapacity,
407                                     LispObject initialElement,
408                                     LispObject initialContents) {
409     if (initialContents != null) {
410       // "If INITIAL-CONTENTS is supplied, it is treated as for MAKE-
411       // ARRAY. In this case none of the original contents of array
412       // appears in the resulting array."
413       ByteBuffer newElements = null;
414       if (directAllocation) {
415         newElements = ByteBuffer.allocateDirect(newCapacity);
416       } else {
417         newElements = ByteBuffer.allocate(newCapacity);
418       }
419 
420       if (initialContents.listp()) {
421         LispObject list = initialContents;
422           for (int i = 0; i < newCapacity; i++) {
423             newElements.put(i, coerceToJavaByte(list.car()));
424             list = list.cdr();
425           }
426       } else if (initialContents.vectorp()) {
427         for (int i = 0; i < newCapacity; i++) {
428           newElements.put(i, coerceToJavaByte(initialContents.elt(i)));
429         }
430       } else {
431           type_error(initialContents, Symbol.SEQUENCE);
432       }
433       elements = newElements;
434 
435     } else {
436       if (elements == null) {
437         // Displaced array. Copy existing elements.
438         if (directAllocation) {
439           elements = ByteBuffer.allocateDirect(newCapacity);
440         } else {
441           elements = ByteBuffer.allocate(newCapacity);
442         }
443         final int limit = Math.min(capacity, newCapacity);
444         for (int i = 0; i < limit; i++) {
445           elements.put(i, coerceToJavaByte(array.AREF(displacement + i)));
446         }
447       } else if (capacity != newCapacity) {
448         ByteBuffer newElements = null;
449         if (directAllocation) {
450           ByteBuffer.allocateDirect(newCapacity);
451         } else {
452           ByteBuffer.allocate(newCapacity);
453         }
454         newElements.put(elements.array(), 0,
455                         Math.min(capacity, newCapacity));
456         elements = newElements;
457       }
458       // Initialize new elements (if applicable).
459       if (initialElement != null) {
460         byte b = coerceToJavaByte(initialElement);
461         for (int i = capacity; i < newCapacity; i++) {
462           elements.put(b);
463         }
464       }
465     }
466     capacity = newCapacity;
467     array = null;
468     displacement = 0;
469     isDisplaced = false;
470     return this;
471   }
472 
473     @Override
adjustArray(int newCapacity, AbstractArray displacedTo, int displacement)474       public AbstractVector adjustArray(int newCapacity,
475                                         AbstractArray displacedTo,
476                                         int displacement) {
477       capacity = newCapacity;
478       array = displacedTo;
479       this.displacement = displacement;
480       elements = null;
481       isDisplaced = true;
482       return this;
483     }
484   }
485