001/* 002 * Copyright 2013 Alex Kasko (alexkasko.com) 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.alexkasko.unsafe.offheapstruct; 018 019import com.alexkasko.unsafe.bytearray.ByteArrayTool; 020import com.alexkasko.unsafe.offheap.OffHeapDisposable; 021import com.alexkasko.unsafe.offheap.OffHeapDisposableIterator; 022import com.alexkasko.unsafe.offheap.OffHeapMemory; 023 024/** 025 * <p>Implementation of off-heap array list of structs (memory areas of equal sizes). 026 * 027 * <p>Default implementation uses {@code sun.misc.Unsafe}, with all operations guarded with {@code assert} keyword. 028 * With assertions enabled in runtime ({@code -ea} java switch) {@link AssertionError} 029 * will be thrown on illegal index access. Without assertions illegal index will crash JVM. 030 * 031 * <p>Array won't be zeroed after creation (will contain garbage by default). 032 * Allocated memory may be freed manually using {@link #free()} (thread-safe 033 * and may be called multiple times) or it will be freed after {@link com.alexkasko.unsafe.offheapstruct.OffHeapStructArray} 034 * instance will be garbage collected. 035 * 036 * 037 * @author alexkasko 038 * Date: 7/3/13 039 */ 040public class OffHeapStructArrayList implements OffHeapStructCollection, OffHeapDisposable { 041 private static final int MIN_CAPACITY_INCREMENT = 12; 042 043 private final ByteArrayTool bt; // not-null only for on-heap arrays 044 private final int structLength; 045 private OffHeapMemory ohm; 046 private long size; 047 private long capacity; 048 049 /** 050 * Constructor with default capacity = {@code 12} 051 * 052 * @param structLength length of the single struct in bytes, must be >= {@code 8} 053 */ 054 public OffHeapStructArrayList(int structLength) { 055 this(MIN_CAPACITY_INCREMENT, structLength); 056 } 057 058 /** 059 * Constructor 060 * 061 * @param capacity initial capacity for this list 062 * @param structLength length of the single struct in bytes, must be >= {@code 8} 063 */ 064 public OffHeapStructArrayList(long capacity, int structLength) { 065 this.bt = null; 066 this.structLength = structLength; 067 this.capacity = capacity; 068 this.ohm = OffHeapMemory.allocateMemory(capacity * structLength); 069 } 070 071 /** 072 * Constructor, uses {@link com.alexkasko.unsafe.offheap.OnHeapMemory} underneath 073 * effectively making this instance an <b>OnHeap</b> collection 074 * 075 * @param bt byte array tool to manage on-heap memory of this collection 076 * @param capacity initial capacity for this list 077 * @param structLength length of struct in bytes, must be >= {@code 8} 078 */ 079 public OffHeapStructArrayList(ByteArrayTool bt, int capacity, int structLength) { 080 this.bt = bt; 081 this.structLength = structLength; 082 this.capacity = capacity; 083 this.ohm = OffHeapMemory.allocateMemoryOnHeap(bt, capacity * structLength); 084 } 085 086 /** 087 * Returns length of the single struct in bytes 088 * 089 * @return length of the single struct in bytes 090 */ 091 @Override 092 public int structLength() { 093 return structLength; 094 } 095 096 /** 097 * Returns number of elements in this list 098 * 099 * @return number of elements in this list 100 */ 101 @Override 102 public long size() { 103 return size; 104 } 105 106 /** 107 * Returns max number of possible elements in this list without memory reallocation 108 * 109 * @return max number of possible elements in this list without memory reallocation 110 */ 111 public long capacity() { 112 return capacity; 113 } 114 115 /** 116 * Frees allocated memory, may be called multiple times from any thread 117 */ 118 @Override 119 public void free() { 120 ohm.free(); 121 } 122 123 /** 124 * {@inheritDoc} 125 */ 126 @Override 127 public OffHeapDisposableIterator<byte[]> iterator() { 128 return new OffHeapStructIterator(this); 129 } 130 131 /** 132 * Whether unsafe implementation of {@link OffHeapMemory} is used 133 * 134 * @return whether unsafe implementation of {@link OffHeapMemory} is used 135 */ 136 public boolean isUnsafe() { 137 return ohm.isUnsafe(); 138 } 139 140 /** 141 * Copies struct on specified index into specified buffer 142 * 143 * @param index array index 144 * @param buffer buffer to copy struct into 145 */ 146 @Override 147 public void get(long index, byte[] buffer) { 148 ohm.get(index * structLength, buffer); 149 } 150 151 /** 152 * Copies struct on specified index into specified buffer 153 * 154 * @param index array index 155 * @param buffer buffer to copy struct into 156 * @param bufferPos start position in specified buffer 157 */ 158 public void get(long index, byte[] buffer, int bufferPos) { 159 ohm.get(index * structLength, buffer, bufferPos, structLength); 160 } 161 162 /** 163 * Copies part of struct on specified index into specified buffer 164 * 165 * @param index array index 166 * @param pos position in struct 167 * @param buffer buffer to copy struct into 168 * @param bufferPos start position in specified buffer 169 * @param length number of bytes to copy 170 */ 171 @Override 172 public void get(long index, int pos, byte[] buffer, int bufferPos, int length) { 173 ohm.get(index * structLength + pos, buffer, bufferPos, length); 174 } 175 176 /** 177 * Copies specified struct contents onto specified index 178 * 179 * @param index array index 180 * @param struct struct to copy into array 181 */ 182 @Override 183 public void set(long index, byte[] struct) { 184 ohm.put(index * structLength, struct); 185 } 186 187 /** 188 * Copies specified struct contents onto specified index 189 * 190 * @param index array index 191 * @param struct struct to copy into array 192 * @param structPos start position in specified struct 193 */ 194 public void set(long index, byte[] struct, int structPos) { 195 ohm.put(index * structLength, struct, structPos, structLength); 196 } 197 198 /** 199 * Gets byte from struct on specified index with specified offset 200 * 201 * @param index array index 202 * @param offset struct offset 203 * @return byte 204 */ 205 @Override 206 public byte getByte(long index, int offset) { 207 assert offset <= structLength - 1 : offset; 208 return ohm.getByte(index * structLength + offset); 209 } 210 211 /** 212 * Puts byte into struct onto specified index with specified offset 213 * 214 * @param index array index 215 * @param offset struct offset 216 * @param value value 217 */ 218 @Override 219 public void putByte(long index, int offset, byte value) { 220 assert offset <= structLength - 1 : offset; 221 ohm.putByte(index * structLength + offset, value); 222 } 223 224 /** 225 * Gets one byte (stored as one signed byte) from struct on specified index 226 * with specified offset, converts it to unsigned and returns it as short 227 * 228 * @param index array index 229 * @param offset struct offset 230 * @return unsigned byte as short 231 */ 232 @Override 233 public short getUnsignedByte(long index, int offset) { 234 assert offset <= structLength - 1 : offset; 235 return ohm.getUnsignedByte(index * structLength + offset); 236 } 237 238 /** 239 * Puts short with value from 0 to 255 inclusive into struct onto specified 240 * index with specified offset as one signed byte 241 * 242 * @param index array index 243 * @param offset struct offset 244 * @param value unsigned byte 245 */ 246 @Override 247 public void putUnsignedByte(long index, int offset, short value) { 248 assert offset <= structLength - 1 : offset; 249 ohm.putUnsignedByte(index * structLength + offset, value); 250 } 251 252 /** 253 * Gets two bytes as short from struct on specified index with specified offset 254 * 255 * @param index array index 256 * @param offset struct offset 257 * @return short value 258 */ 259 @Override 260 public short getShort(long index, int offset) { 261 assert offset <= structLength - 2 : offset; 262 return ohm.getShort(index * structLength + offset); 263 } 264 265 /** 266 * Puts short into struct onto specified index with specified offset as two bytes 267 * 268 * @param index array index 269 * @param offset struct offset 270 * @param value short value 271 */ 272 @Override 273 public void putShort(long index, int offset, short value) { 274 assert offset <= structLength - 2 : offset; 275 ohm.putShort(index * structLength + offset, value); 276 } 277 278 /** 279 * Gets unsigned short (stored as two bytes) from struct on specified index 280 * with specified offset and returns it as int 281 * 282 * @param index array index 283 * @param offset struct offset 284 * @return unsigned short as int 285 */ 286 @Override 287 public int getUnsignedShort(long index, int offset) { 288 assert offset <= structLength - 2 : offset; 289 return ohm.getUnsignedShort(index * structLength + offset); 290 } 291 292 /** 293 * Puts int with value from 0 to 65535 inclusive into struct onto specified 294 * index with specified offset as two bytes 295 * 296 * @param index array index 297 * @param offset struct offset 298 * @param value unsigned short as int 299 */ 300 @Override 301 public void putUnsignedShort(long index, int offset, int value) { 302 assert offset <= structLength - 2 : offset; 303 ohm.putUnsignedShort(index * structLength + offset, value); 304 } 305 306 /** 307 * Gets four bytes as int from struct on specified index with specified offset 308 * 309 * @param index array index 310 * @param offset struct offset 311 * @return int value 312 */ 313 @Override 314 public int getInt(long index, int offset) { 315 assert offset <= structLength - 4 : offset; 316 return ohm.getInt(index * structLength + offset); 317 } 318 319 /** 320 * Puts int into struct onto specified index with specified offset as four bytes 321 * 322 * @param index array index 323 * @param offset struct offset 324 * @param value int value 325 */ 326 @Override 327 public void putInt(long index, int offset, int value) { 328 assert offset <= structLength - 4 : offset; 329 ohm.putInt(index * structLength + offset, value); 330 } 331 332 /** 333 * Gets unsigned int (stored as 4 bytes) and returns it as long 334 * from struct on specified index with specified offset 335 * 336 * @param index array index 337 * @param offset struct offset 338 * @return unsigned int as long 339 */ 340 @Override 341 public long getUnsignedInt(long index, int offset) { 342 assert offset <= structLength - 4 : offset; 343 return ohm.getUnsignedInt(index * structLength + offset); 344 } 345 346 /** 347 * Puts long value from 0 to 4294967295 inclusive into struct onto specified index 348 * with specified offset as four bytes 349 * 350 * @param index array index 351 * @param offset struct offset 352 * @param value unsigned int as long 353 */ 354 @Override 355 public void putUnsignedInt(long index, int offset, long value) { 356 assert offset <= structLength - 4 : offset; 357 ohm.putUnsignedInt(index * structLength + offset, value); 358 } 359 360 /** 361 * Gets long from struct on specified index with specified offset 362 * 363 * @param index array index 364 * @param offset struct offset 365 * @return long value 366 */ 367 @Override 368 public long getLong(long index, int offset) { 369 assert offset <= structLength - 8 : offset; 370 return ohm.getLong(index * structLength + offset); 371 } 372 373 /** 374 * Puts long into struct onto specified index with specified offset as eight bytes 375 * 376 * @param index array index 377 * @param offset struct offset 378 * @param value long value 379 */ 380 @Override 381 public void putLong(long index, int offset, long value) { 382 assert offset <= structLength - 8 : offset; 383 ohm.putLong(index * structLength + offset, value); 384 } 385 386 /** 387 * Adds element to the end of this list. Memory area will be allocated another time and copied 388 * on capacity exceed. 389 * 390 * @param struct to add 391 */ 392 public void add(byte[] struct) { 393 add(struct, 0); 394 } 395 396 /** 397 * Adds element to the end of this list. Memory area will be allocated another time and copied 398 * on capacity exceed. 399 * 400 * @param struct struct to add 401 * @param structPos struct offset 402 */ 403 public void add(byte[] struct, int structPos) { 404 OffHeapMemory oh = ohm; 405 long s = size; 406 if (s == capacity) { 407 long len = s + (s < (MIN_CAPACITY_INCREMENT / 2) ? MIN_CAPACITY_INCREMENT : s >> 1); 408 OffHeapMemory newOhm = null == bt ? OffHeapMemory.allocateMemory(len * structLength) 409 : OffHeapMemory.allocateMemoryOnHeap(bt, len * structLength); 410 // maybe it's better to use Unsafe#reallocateMemory here 411 oh.copy(0, newOhm, 0, oh.length()); 412 oh.free(); 413 ohm = newOhm; 414 capacity = len; 415 } 416 size = s + 1; 417 set(s, struct, structPos); 418 } 419 420 /** 421 * Resets the collection setting size to 0. 422 * Actual memory contents stays untouched. 423 */ 424 public void reset() { 425 this.size = 0; 426 } 427 428 /** 429 * Shrinks array list capacity to current size 430 */ 431 public void shrinkToFit() { 432 long byteslen = size * structLength; 433 OffHeapMemory oh = ohm; 434 OffHeapMemory newOhm = null == bt ? OffHeapMemory.allocateMemory(byteslen) 435 : OffHeapMemory.allocateMemoryOnHeap(bt, byteslen); 436 oh.copy(0, newOhm, 0, byteslen); 437 oh.free(); 438 ohm = newOhm; 439 capacity = size; 440 } 441 442 /** 443 * {@inheritDoc} 444 */ 445 @Override 446 public String toString() { 447 final StringBuilder sb = new StringBuilder(); 448 sb.append("OffHeapStructArrayList"); 449 sb.append("{structLength=").append(structLength); 450 sb.append(", ohm=").append(ohm); 451 sb.append(", size=").append(size); 452 sb.append(", capacity=").append(capacity); 453 sb.append('}'); 454 return sb.toString(); 455 } 456}