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.offheaplong;
018
019import com.alexkasko.unsafe.offheap.OffHeapDisposable;
020import com.alexkasko.unsafe.offheap.OffHeapDisposableIterator;
021import com.alexkasko.unsafe.offheap.OffHeapMemory;
022
023import java.util.Iterator;
024
025/**
026 * <p>Implementation of array-list of long using {@link com.alexkasko.unsafe.offheap.OffHeapMemory}.
027 * Memory area will be allocated another time and copied on elements adding. This class doesn't support elements removing.
028 * {@link #get(long)} and {@link #set(long, long)} access operations indexes are checked using {@code assert} keyword
029 * (indexes between size and capacity will be rejected).
030 *
031 * <p>Default implementation uses {@code sun.misc.Unsafe}, with all operations guarded with {@code assert} keyword.
032 * With assertions enabled in runtime ({@code -ea} java switch) {@link AssertionError}
033 * will be thrown on illegal index access. Without assertions illegal index will crash JVM.
034 *
035 * <p>Allocated memory may be freed manually using {@link #free()} (thread-safe
036 * and may be called multiple times) or it will be freed after {@link OffHeapLongArray}
037 * will be garbage collected.
038 *
039 * <p>Note: while class implements Iterable, iterator will create new autoboxed Long object
040 * <b>on every</b> {@code next()} call, this behaviour is inevitable with iterators in java 6/7.
041 *
042 * @author alexkasko
043 *         Date: 3/1/13
044 */
045public class OffHeapLongArrayList implements OffHeapLongAddressable, OffHeapDisposable, Iterable<Long> {
046    private static final int MIN_CAPACITY_INCREMENT = 12;
047    private static final int ELEMENT_LENGTH = 8;
048
049    private OffHeapMemory ohm;
050    private long size;
051    private long capacity;
052
053    /**
054     * Constructor, {@code 12} is used as initial capacity
055     */
056    public OffHeapLongArrayList() {
057        this(MIN_CAPACITY_INCREMENT);
058    }
059
060    /**
061     * Constructor
062     *
063     * @param capacity initial capacity
064     */
065    public OffHeapLongArrayList(long capacity) {
066        this.capacity = capacity;
067        this.ohm = OffHeapMemory.allocateMemory(capacity * ELEMENT_LENGTH);
068    }
069
070    /**
071     * Adds element to the end of this list. Memory area will be allocated another time and copied
072     * on capacity exceed.
073     *
074     * @param value value to add
075     */
076    public void add(long value) {
077        OffHeapMemory oh = ohm;
078        long s = size;
079        if (s == capacity) {
080            long len = s + (s < (MIN_CAPACITY_INCREMENT / 2) ? MIN_CAPACITY_INCREMENT : s >> 1);
081            OffHeapMemory newOhm = OffHeapMemory.allocateMemory(len * ELEMENT_LENGTH);
082            // maybe it's better to use Unsafe#reallocateMemory here
083            oh.copy(0, newOhm, 0, oh.length());
084            oh.free();
085            ohm = newOhm;
086            capacity = len;
087        }
088        size = s + 1;
089        set(s, value);
090    }
091
092    /**
093     * Whether unsafe implementation of {@link OffHeapMemory} is used
094     *
095     * @return whether unsafe implementation of {@link OffHeapMemory} is used
096     */
097    public boolean isUnsafe() {
098        return ohm.isUnsafe();
099    }
100
101    /**
102     * Gets the element at position {@code index} from {@code 0} to {@code size-1}
103     *
104     * @param index list index
105     * @return long value
106     */
107    @Override
108    public long get(long index) {
109        assert index < size : index;
110        return ohm.getLong(index * ELEMENT_LENGTH);
111    }
112
113    /**
114     * Sets the element at position {@code index} (from {@code 0} to {@code size-1}) to the given value
115     *
116     * @param index list index
117     * @param value long value
118     */
119    @Override
120    public void set(long index, long value) {
121        assert index < size : index;
122        ohm.putLong(index * ELEMENT_LENGTH, value);
123    }
124
125    /**
126     * Returns number of elements in list
127     *
128     * @return number of elements in list
129     */
130    @Override
131    public long size() {
132        return size;
133    }
134
135    /**
136     * Returns number of elements list may contain without additional memory allocation
137     *
138     * @return number of elements list may contain without additional memory allocation
139     */
140    public long capacity() {
141        return capacity;
142    }
143
144    /**
145     * Frees allocated memory, may be called multiple times from any thread
146     */
147    @Override
148    public void free() {
149        ohm.free();
150    }
151
152    /**
153     * {@inheritDoc}
154     */
155    @Override
156    public OffHeapDisposableIterator<Long> iterator() {
157        return new OffHeapLongIterator(this);
158    }
159
160    /**
161     * Resets the collection setting size to 0.
162     * Actual memory contents stays untouched.
163     */
164    public void reset() {
165        this.size = 0;
166    }
167
168    /**
169     * {@inheritDoc}
170     */
171    @Override
172    public String toString() {
173        final StringBuilder sb = new StringBuilder();
174        sb.append("OffHeapLongArrayList");
175        sb.append("{size=").append(size());
176        sb.append(", capacity=").append(capacity);
177        sb.append(", unsafe=").append(isUnsafe());
178        sb.append('}');
179        return sb.toString();
180    }
181}