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 package com.imagero.uio.bio;
033
034 import com.imagero.util.OpenVector;
035 import com.imagero.uio.bio.content.SynchronizedContent;
036 import com.imagero.uio.bio.content.Content;
037 import com.imagero.uio.UIOStreamBuilder;
038
039 import java.io.DataOutput;
040 import java.io.IOException;
041 import java.io.OutputStream;
042 import java.util.Enumeration;
043 import java.util.NoSuchElementException;
044
045 /**
046 * Buffer controller.
047 * IOController loads data from Content and maintains buffers (FixedSizeBuffer).
048 * @author Andrey Kuznetsov
049 */
050 public class IOController {
051
052 OpenVector bufs = new OpenVector(100);
053
054 int bufferSize = UIOStreamBuilder.DEFAULT_CHUNK_SIZE;
055 int arrayLength = 1000;
056
057 Content content;
058
059 RingSpeicher rs;
060 int maxBufferCount = UIOStreamBuilder.DEFAULT_CHUNK_COUNT;
061
062 long explicitLength;
063
064 public IOController(int bufferSize, Content content) {
065 this.bufferSize = bufferSize;
066 this.content = content;
067 this.rs = new RingSpeicher(maxBufferCount);
068 }
069
070 final void setLength(long newLength) throws IOException {
071 explicitLength = newLength;
072 }
073
074 private Enumeration buffers(final boolean allowNullValues) {
075 return new Enumeration() {
076 final FixedSizeByteBuffer empty = new FixedSizeByteBuffer(new byte[bufferSize]);
077 final long length = length();
078 final BufferIndex max = getBufferIndex(length);
079 final BufferIndex bi = new BufferIndex(0, 0);
080
081 public boolean hasMoreElements() {
082 boolean b = bi.arrayIndex < max.arrayIndex;
083 boolean b2 = bi.index <= max.index;
084 return b || b2;
085 }
086
087 public Object nextElement() {
088 if (hasMoreElements()) {
089 if (!(bi.index < arrayLength)) {
090 bi.index = 0;
091 bi.arrayIndex++;
092 }
093 FixedSizeByteBuffer fb = getBuffer(bi.arrayIndex, bi.index++);
094 return fb != null || allowNullValues ? fb : empty;
095 } else {
096 throw new NoSuchElementException();
097 }
098 }
099 };
100 }
101
102 private Enumeration buffers(final long maxPos, final boolean allowNullValues) {
103 return new Enumeration() {
104 final FixedSizeByteBuffer empty = new FixedSizeByteBuffer(new byte[bufferSize]);
105 final long length = Math.min(maxPos, length());
106 final BufferIndex max = getBufferIndex(length);
107 final BufferIndex bi = new BufferIndex(0, 0);
108
109 public boolean hasMoreElements() {
110 boolean b = bi.arrayIndex < max.arrayIndex;
111 boolean b2 = bi.index <= max.index;
112 return b || b2;
113 }
114
115 public Object nextElement() {
116 if (hasMoreElements()) {
117 if (!(bi.index < arrayLength)) {
118 bi.index = 0;
119 bi.arrayIndex++;
120 }
121 FixedSizeByteBuffer fb = getBuffer(bi.arrayIndex, bi.index++);
122 return fb != null || allowNullValues ? fb : empty;
123 } else {
124 throw new NoSuchElementException();
125 }
126 }
127 };
128 }
129
130
131 long length() {
132 if (explicitLength > 0) {
133 return explicitLength;
134 }
135
136 long contentLength = 0;
137 try {
138 contentLength = content.length();
139 } catch (IOException ex) {
140 ex.printStackTrace();
141 }
142
143 Object[] elements = bufs.getElements();
144 int maxI = elements.length - 1;
145 for (int i = maxI; i >= 0; i--) {
146 BufferArray ba = (BufferArray) elements[i];
147 if (ba != null) {
148 FixedSizeByteBuffer[] buffers = ba.buffers;
149 int maxJ = buffers.length - 1;
150 for (int j = maxJ; j >= 0; j--) {
151 FixedSizeByteBuffer buffer = buffers[j];
152 if (buffer != null) {
153 int count = buffer.getCount();
154 long startOffset = getStartOffset(buffer.index, bufferSize);
155 if (count > 0) {
156 return Math.max(contentLength, startOffset + count);
157 }
158 }
159 }
160 }
161 }
162 return contentLength;
163 }
164
165 void writeTo(OutputStream out) throws IOException {
166 Enumeration enum = buffers(false);
167 while (enum.hasMoreElements()) {
168 FixedSizeByteBuffer buffer = (FixedSizeByteBuffer) enum.nextElement();
169 buffer.writeBuffer(out, enum.hasMoreElements());
170 }
171 }
172
173 void writeTo(DataOutput out) throws IOException {
174 Enumeration enum = buffers(false);
175 while (enum.hasMoreElements()) {
176 FixedSizeByteBuffer buffer = (FixedSizeByteBuffer) enum.nextElement();
177 buffer.writeBuffer(out, enum.hasMoreElements());
178 }
179 }
180
181 private class BufferArray {
182 FixedSizeByteBuffer[] buffers;
183
184 public BufferArray() {
185 buffers = new FixedSizeByteBuffer[arrayLength];
186 }
187 }
188
189 private FixedSizeByteBuffer getBuffer(BufferIndex bi) {
190 return getBuffer(bi.arrayIndex, bi.index);
191 }
192
193 private FixedSizeByteBuffer getBuffer(int aIndex, int index) {
194 Object[] objects = bufs.checkSize(aIndex);
195 BufferArray ba = (BufferArray) objects[aIndex];
196 if (ba == null) {
197 ba = new BufferArray();
198 objects[aIndex] = ba;
199 }
200 return ba.buffers[index];
201 }
202
203 protected void setBuffer(BufferIndex index, FixedSizeByteBuffer buffer) {
204 Object[] objects = bufs.checkSize(index.arrayIndex);
205 BufferArray ba = (BufferArray) objects[index.arrayIndex];
206 ba.buffers[index.index] = buffer;
207 }
208
209 public long flushBefore(long pos) {
210 pos = (pos / bufferSize) * bufferSize;
211 Enumeration enum = buffers(pos, true);
212 while (enum.hasMoreElements()) {
213 FixedSizeByteBuffer o = (FixedSizeByteBuffer) enum.nextElement();
214 if (o != null) {
215 o.buf = null;
216 }
217 }
218 return pos;
219 }
220
221 public BufferIndex getBufferIndex(long pos) {
222 long count = pos / bufferSize;
223 long aIndex = count / arrayLength;
224 int index = (int) (count % arrayLength);
225 if (aIndex > Integer.MAX_VALUE) {
226 throw new IndexOutOfBoundsException("Please increase buffer size");
227 }
228 BufferIndex bi = new BufferIndex((int) aIndex, index);
229 return bi;
230 }
231
232 public FixedSizeByteBuffer getBuffer(long pos) {
233 return getBuffer(getBufferIndex(pos));
234 }
235
236 FixedSizeByteBuffer getBuffer(long pos, boolean load) throws IOException {
237 BufferIndex bi = getBufferIndex(pos);
238 long startOffset = getStartOffset(bi, bufferSize);
239 FixedSizeByteBuffer sb = getBuffer(bi);
240 if (sb == null) {
241 sb = new FixedSizeByteBuffer(new byte[bufferSize]);
242 setBuffer(bi, sb);
243 sb.index = bi;
244 if (load) {
245 long max = content.length();
246 if (pos > max) {
247 return null;
248 }
249
250 int size = content.load(startOffset, sb.buf);
251 sb.count = size;
252 }
253 if (content.canReload()) {
254 checkBuffers(sb);
255 }
256 } else {
257 if (sb.buf == null) {
258 long max = content.length();
259 if (pos > max) {
260 return null;
261 }
262 sb.buf = new byte[bufferSize];
263 int size = content.load(startOffset, sb.buf);
264 sb.count = size;
265 }
266 }
267 return sb;
268 }
269
270 private void checkBuffers(FixedSizeByteBuffer buffer0) {
271 FixedSizeByteBuffer buffer = (FixedSizeByteBuffer) rs.add(buffer0);
272 if (buffer != null && content.writable()) {
273 if (buffer.changed) {
274 try {
275 long offset = getStartOffset(buffer.index, bufferSize);
276 content.save(offset, 0, buffer.buf, buffer.getCount());
277 buffer.changed = false;
278 } catch (IOException ex) {
279 ex.printStackTrace();
280 }
281 }
282 buffer.buf = null;
283 }
284 }
285
286 void sync() throws IOException {
287 boolean canWrite = content.writable();
288 if (!canWrite) {
289 return;
290 }
291 /*long length = */content.length(); //may be already closed
292
293 Enumeration enum = buffers(true);
294 while (enum.hasMoreElements()) {
295 FixedSizeByteBuffer buffer = (FixedSizeByteBuffer) enum.nextElement();
296 if (buffer != null && buffer.changed) {
297 long offset = getStartOffset(buffer.index, bufferSize);
298 content.save(offset, 0, buffer.buf, buffer.getCount());
299 buffer.changed = false;
300 }
301 }
302 }
303
304 public long getStartOffset(BufferIndex bufferIndex, int bufferSize) {
305 long res = bufferIndex.arrayIndex * arrayLength * bufferSize;
306 res += bufferIndex.index * bufferSize;
307 return res;
308 }
309
310 /**
311 * determine if access to stream content is synchronized
312 */
313 public boolean isSynchronizedContent() {
314 return content instanceof SynchronizedContent;
315 }
316
317 /**
318 * define if access to content should be syncronized.
319 */
320 public void setSynchronizedContent(boolean b) {
321 if(b) {
322 if(!isSynchronizedContent()) {
323 content = new SynchronizedContent(content);
324 }
325 }
326 else {
327 if(isSynchronizedContent()) {
328 SynchronizedContent synchronizedContent = (SynchronizedContent) content;
329 content = synchronizedContent.getContent();
330 }
331 }
332 }
333
334 protected void finalize() throws Throwable {
335 super.finalize();
336 content = null;
337 rs = null;
338 bufs = null;
339 }
340 }