001    /*
002     * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
003     *
004     * http://uio.imagero.com
005     *
006     * Redistribution and use in source and binary forms, with or without
007     * modification, are permitted provided that the following conditions are met:
008     *
009     *  o Redistributions of source code must retain the above copyright notice,
010     *    this list of conditions and the following disclaimer.
011     *
012     *  o Redistributions in binary form must reproduce the above copyright notice,
013     *    this list of conditions and the following disclaimer in the documentation
014     *    and/or other materials provided with the distribution.
015     *
016     *  o Neither the name of Andrey Kuznetsov nor the names of
017     *    its contributors may be used to endorse or promote products derived
018     *    from this software without specific prior written permission.
019     *
020     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
021     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
022     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
023     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
024     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
025     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
026     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
027     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
028     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
029     * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
030     * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031     */
032    
033    package com.imagero.uio.impl.array;
034    
035    import com.imagero.uio.bio.BufferPosition;
036    import com.imagero.uio.bio.IOController;
037    import com.imagero.uio.bio.IOCInputStream;
038    import com.imagero.uio.bio.IOCOutputStream;
039    import com.imagero.uio.impl.AbstractRandomAccessIO;
040    
041    import java.io.EOFException;
042    import java.io.IOException;
043    import java.io.InputStream;
044    import java.io.OutputStream;
045    
046    /**
047     * @author Andrey Kuznetsov
048     */
049    public abstract class AbstractRandomAccessArray extends AbstractRandomAccessIO {
050    
051        BufferPosition fp;
052        IOController controller;
053    
054        int length;
055        int offset;
056    
057        protected int[] BE_SHIFTS;
058        protected int[] LE_SHIFTS;
059        protected int[] shifts;
060    
061        final int bytesPerUnit = getBytesPerUnit();
062        final int andMask = getAndMask();
063    
064    
065        public AbstractRandomAccessArray(int offset, int length, int byteOrder) {
066            this(offset, length, byteOrder, new BufferPosition(Integer.MAX_VALUE));
067        }
068    
069        public AbstractRandomAccessArray(int offset, int length, int byteOrder, BufferPosition fp) {
070            this.offset = offset;
071            this.fp = fp != null ? fp : new BufferPosition(Integer.MAX_VALUE);
072            BE_SHIFTS = createShiftBE();
073            LE_SHIFTS = createShiftLE();
074            setByteOrder(byteOrder);
075            this.length = length * bytesPerUnit;
076        }
077    
078        public void setByteOrder(int byteOrder) {
079            super.setByteOrder(byteOrder);
080            if (byteOrder == BIG_ENDIAN) {
081                shifts = BE_SHIFTS;
082            } else if (byteOrder == LITTLE_ENDIAN) {
083                shifts = LE_SHIFTS;
084            }
085        }
086    
087        protected abstract int[] createShiftBE();
088    
089        protected abstract int[] createShiftLE();
090    
091        protected abstract int getBytesPerUnit();
092    
093        protected abstract int getAndMask();
094    
095        public long getFilePointer() {
096            return fp.pos - offset;
097        }
098    
099        public long length() {
100            return length - offset;
101        }
102    
103        public void seek(long offset) throws IOException {
104            if (offset + this.offset > Integer.MAX_VALUE) {
105                throw new IOException("Can't seek so far");
106            }
107            fp.pos = (int) offset + this.offset;
108        }
109    
110        public long skip(long n) throws IOException {
111            long k = Math.min(fp.pos + n, length);//todo check conditions
112            fp.pos += k;
113            return k;
114        }
115    
116        public void close() throws IOException {
117        }
118    
119        public int read(byte[] b, int off, int len) throws IOException {
120            final int max = (int) Math.min(len, length() - fp.pos);//todo check conditions
121            if (max <= 0) {
122                return -1;
123            }
124            len = max;
125            while ((fp.pos & andMask) != 0) {
126                b[off++] = (byte) read();
127                len--;
128            }
129            int count = len / bytesPerUnit;
130            if (byteOrder == BIG_ENDIAN) {
131                readBE(/*offset + */fp.pos / bytesPerUnit, count, b, offset);
132            } else {
133                readLE(/*offset + */fp.pos / bytesPerUnit, count, b, offset);
134            }
135            int read = count * bytesPerUnit;
136            fp.pos += read;
137            off += read;
138            len -= read;
139            while (len-- > 0) {
140                b[off++] = (byte) read();
141            }
142            return max;
143        }
144    
145        protected abstract void readBE(int srcOffset, int count, byte[] dest, int off);
146    
147        protected abstract void readLE(int srcOffset, int count, byte[] dest, int off);
148    
149    
150        public void setLength(long newLength) throws IOException {
151        }
152    
153        public void write(byte b[], int off, int len) throws IOException {
154            len = (int) Math.min(len, length() - fp.pos);//todo check conditions
155            if (len <= 0) {
156                throw  new EOFException();
157            }
158            if ((fp.pos & andMask) != 0) {
159                write(b[off++]);
160                len--;
161            }
162            int l2 = len / bytesPerUnit;
163            write(b, off, len, fp.pos / bytesPerUnit, byteOrder == BIG_ENDIAN);
164            fp.pos += l2 * bytesPerUnit;
165            if ((len & andMask) != 0) {
166                write(b[off + len - 1]);
167            }
168        }
169    
170        protected abstract void write(byte[] src, int srcOffset, int len, int destOffset, boolean bigEndian);
171    
172        protected final long combine(long v, int b) {
173            int shift = shifts[(fp.pos & andMask)];
174            return (v & (0xFF << shift)) | ((b & 0xFF) << shift);
175        }
176    
177        protected final int combine(int v, int b) {
178            int shift = shifts[(fp.pos & andMask)];
179            return (v & (0xFF << shift)) | ((b & 0xFF) << shift);
180        }
181    
182        protected final int _read() throws IOException {
183            int k = read();
184            if (k == -1) {
185                throw new EOFException();
186            }
187            return k;
188        }
189    
190        protected abstract IOController createController();
191    
192        public IOController getController() {
193            if(controller == null) {
194                controller = createController();
195            }
196            return controller;
197        }
198    
199        public InputStream createInputStream(long offset) {
200            return new IOCInputStream(getController(), offset);
201        }
202    
203        public OutputStream createOutputStream(long offset) {
204            return new IOCOutputStream(getController(), offset);
205        }
206    }