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