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 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 * @author alexkasko
037 * Date: 7/3/13
038 */
039public class OffHeapStructArray implements OffHeapStructCollection, OffHeapDisposable {
040
041    private final OffHeapMemory ohm;
042    private final int structLength;
043
044    /**
045      * Constructor
046      *
047      * @param size array size
048      * @param structLength length of struct in bytes, must be >= {@code 8}
049      */
050    public OffHeapStructArray(long size, int structLength) {
051        if(structLength < 8) throw new IllegalArgumentException(
052                "structLength must be greater or equal to 8, but was: [" + structLength + "]");
053        this.structLength = structLength;
054        this.ohm = OffHeapMemory.allocateMemory(size * structLength);
055    }
056
057    /**
058     * Constructor, uses {@link com.alexkasko.unsafe.offheap.OnHeapMemory} underneath
059     * effectively making this instance an <b>OnHeap</b> collection
060     *
061     * @param bt byte array tool to manage on-heap memory of this collection
062     * @param size array size
063     * @param structLength length of struct in bytes, must be >= {@code 8}
064     */
065    public OffHeapStructArray(ByteArrayTool bt, int size, int structLength) {
066        if(structLength < 8) throw new IllegalArgumentException(
067                "structLength must be greater or equal to 8, but was: [" + structLength + "]");
068        this.structLength = structLength;
069        this.ohm = OffHeapMemory.allocateMemoryOnHeap(bt, size * structLength);
070    }
071
072    /**
073     * Private constructor for {@link #clone()} support
074     *
075     * @param ohm cloned memory
076     * @param structLength struct length
077     */
078    private OffHeapStructArray(OffHeapMemory ohm, int structLength) {
079        this.ohm = ohm;
080        this.structLength = structLength;
081    }
082
083    /**
084     * Returns length of the single struct in bytes
085     *
086     * @return length of the single struct in bytes
087     */
088    @Override
089    public int structLength() {
090        return structLength;
091    }
092
093    /**
094     * Returns number of elements in this array
095     *
096     * @return number of elements in this array
097     */
098    @Override
099    public long size() {
100        return ohm.length() / structLength;
101    }
102
103    /**
104     * Frees allocated memory, may be called multiple times from any thread
105     */
106    @Override
107    public void free() {
108        ohm.free();
109    }
110
111    /**
112     * {@inheritDoc}
113     */
114    @Override
115    public OffHeapDisposableIterator<byte[]> iterator() {
116        return new OffHeapStructIterator(this);
117    }
118
119    /**
120     * Whether unsafe implementation of {@link OffHeapMemory} is used
121     *
122     * @return whether unsafe implementation of {@link OffHeapMemory} is used
123     */
124    public boolean isUnsafe() {
125        return ohm.isUnsafe();
126    }
127
128    /**
129     * Copies struct on specified index into specified buffer
130     *
131     * @param index array index
132     * @param buffer buffer to copy struct into
133     */
134    @Override
135    public void get(long index, byte[] buffer) {
136        ohm.get(index * structLength, buffer);
137    }
138
139    /**
140     * Copies struct on specified index into specified buffer
141     *
142     * @param index array index
143     * @param buffer buffer to copy struct into
144     * @param bufferPos start position in specified buffer
145     */
146    public void get(long index, byte[] buffer, int bufferPos) {
147        ohm.get(index * structLength, buffer, bufferPos, structLength);
148    }
149
150    /**
151     * Copies part of struct on specified index into specified buffer
152     *
153     * @param index array index
154     * @param pos position in struct
155     * @param buffer buffer to copy struct into
156     * @param bufferPos start position in specified buffer
157     * @param length number of bytes to copy
158     */
159    @Override
160    public void get(long index, int pos, byte[] buffer, int bufferPos, int length) {
161        ohm.get(index * structLength + pos, buffer, bufferPos, length);
162    }
163
164    /**
165     * Copies specified struct contents onto specified index
166     *
167     * @param index array index
168     * @param struct struct to copy into array
169     */
170    @Override
171    public void set(long index, byte[] struct) {
172        ohm.put(index * structLength, struct);
173    }
174
175    /**
176     * Copies specified struct contents onto specified index
177     *
178     * @param index array index
179     * @param struct struct to copy into array
180     * @param structPos start position in specified struct
181     */
182    public void set(long index, byte[] struct, int structPos) {
183        ohm.put(index * structLength, struct, structPos, structLength);
184    }
185
186    /**
187     * Gets byte from struct on specified index with specified offset
188     *
189     * @param index  array index
190     * @param offset struct offset
191     * @return byte
192     */
193    @Override
194    public byte getByte(long index, int offset) {
195        assert offset <= structLength - 1 : offset;
196        return ohm.getByte(index * structLength + offset);
197    }
198
199    /**
200     * Puts byte into struct onto specified index with specified offset
201     *
202     * @param index  array index
203     * @param offset struct offset
204     * @param value  value
205     */
206    @Override
207    public void putByte(long index, int offset, byte value) {
208        assert offset <= structLength - 1 : offset;
209        ohm.putByte(index * structLength + offset, value);
210    }
211
212    /**
213     * Gets one byte (stored as one signed byte) from struct on specified index
214     * with specified offset, converts it to unsigned and returns it as short
215     *
216     * @param index  array index
217     * @param offset struct offset
218     * @return unsigned byte as short
219     */
220    @Override
221    public short getUnsignedByte(long index, int offset) {
222        assert offset <= structLength - 1 : offset;
223        return ohm.getUnsignedByte(index * structLength + offset);
224    }
225
226    /**
227     * Puts short with value from 0 to 255 inclusive into struct onto specified
228     * index with specified offset as one signed byte
229     *
230     * @param index  array index
231     * @param offset struct offset
232     * @param value  unsigned byte
233     */
234    @Override
235    public void putUnsignedByte(long index, int offset, short value) {
236        assert offset <= structLength - 1 : offset;
237        ohm.putUnsignedByte(index * structLength + offset, value);
238    }
239
240    /**
241     * Gets two bytes as short from struct on specified index with specified offset
242     *
243     * @param index  array index
244     * @param offset struct offset
245     * @return short value
246     */
247    @Override
248    public short getShort(long index, int offset) {
249        assert offset <= structLength - 2 : offset;
250        return ohm.getShort(index * structLength + offset);
251    }
252
253    /**
254     * Puts short into struct onto specified index with specified offset as two bytes
255     *
256     * @param index  array index
257     * @param offset struct offset
258     * @param value  short value
259     */
260    @Override
261    public void putShort(long index, int offset, short value) {
262        assert offset <= structLength - 2 : offset;
263        ohm.putShort(index * structLength + offset, value);
264    }
265
266    /**
267     * Gets unsigned short (stored as two bytes) from struct on specified index
268     * with specified offset and returns it as int
269     *
270     * @param index  array index
271     * @param offset struct offset
272     * @return unsigned short as int
273     */
274    @Override
275    public int getUnsignedShort(long index, int offset) {
276        assert offset <= structLength - 2 : offset;
277        return ohm.getUnsignedShort(index * structLength + offset);
278    }
279
280    /**
281     * Puts int with value from 0 to 65535 inclusive into struct onto specified
282     * index with specified offset as two bytes
283     *
284     * @param index  array index
285     * @param offset struct offset
286     * @param value  unsigned short as int
287     */
288    @Override
289    public void putUnsignedShort(long index, int offset, int value) {
290        assert offset <= structLength - 2 : offset;
291        ohm.putUnsignedShort(index * structLength + offset, value);
292    }
293
294    /**
295     * Gets four bytes as int from struct on specified index with specified offset
296     *
297     * @param index  array index
298     * @param offset struct offset
299     * @return int value
300     */
301    @Override
302    public int getInt(long index, int offset) {
303        assert offset <= structLength - 4 : offset;
304        return ohm.getInt(index * structLength + offset);
305    }
306
307    /**
308     * Puts int into struct onto specified index with specified offset as four bytes
309     *
310     * @param index  array index
311     * @param offset struct offset
312     * @param value  int value
313     */
314    @Override
315    public void putInt(long index, int offset, int value) {
316        assert offset <= structLength - 4 : offset;
317        ohm.putInt(index * structLength + offset, value);
318    }
319
320    /**
321     * Gets unsigned int (stored as 4 bytes) and returns it as long
322     * from struct on specified index with specified offset
323     *
324     * @param index  array index
325     * @param offset struct offset
326     * @return unsigned int as long
327     */
328    @Override
329    public long getUnsignedInt(long index, int offset) {
330        assert offset <= structLength - 4 : offset;
331        return ohm.getUnsignedInt(index * structLength + offset);
332    }
333
334    /**
335     * Puts long value from 0 to 4294967295 inclusive into struct onto specified index
336     * with specified offset as four bytes
337     *
338     * @param index  array index
339     * @param offset struct offset
340     * @param value  unsigned int as long
341     */
342    @Override
343    public void putUnsignedInt(long index, int offset, long value) {
344        assert offset <= structLength - 4 : offset;
345        ohm.putUnsignedInt(index * structLength + offset, value);
346    }
347
348    /**
349     * Gets long from struct on specified index with specified offset
350     *
351     * @param index  array index
352     * @param offset struct offset
353     * @return long value
354     */
355    @Override
356    public long getLong(long index, int offset) {
357        assert offset <= structLength - 8 : offset;
358        return ohm.getLong(index * structLength + offset);
359    }
360
361    /**
362     * Puts long into struct onto specified index with specified offset as eight bytes
363     *
364     * @param index  array index
365     * @param offset struct offset
366     * @param value  long value
367     */
368    @Override
369    public void putLong(long index, int offset, long value) {
370        assert offset <= structLength - 8 : offset;
371        ohm.putLong(index * structLength + offset, value);
372    }
373
374    /**
375     * {@inheritDoc}
376     */
377    @Override
378    public OffHeapStructArray clone() {
379        OffHeapMemory cloned = ohm.clone();
380        return new OffHeapStructArray(cloned, structLength);
381    }
382
383    /**
384     * {@inheritDoc}
385     */
386    @Override
387    public String toString() {
388        final StringBuilder sb = new StringBuilder();
389        sb.append("OffHeapStructArray");
390        sb.append("{ohm=").append(ohm);
391        sb.append(", structLength=").append(structLength);
392        sb.append('}');
393        return sb.toString();
394    }
395}