Begin versioning.
[fits.git] / src / nom / tam / fits / FitsHeap.java
1 package nom.tam.fits;
2
3 import nom.tam.util.*;
4 import java.io.*;
5
6 /** This class supports the FITS heap.  This
7  *  is currently used for variable length columns
8  *  in binary tables.
9  */
10 public class FitsHeap implements FitsElement {
11
12     /** The storage buffer */
13     private byte[] heap;
14     /** The current used size of the buffer <= heap.length */
15     private int heapSize;
16     /** The offset within a file where the heap begins */
17     private long fileOffset = -1;
18     /** Has the heap ever been expanded? */
19     private boolean expanded = false;
20     /** The stream the last read used */
21     private ArrayDataInput input;
22     /** Our current offset into the heap.  When we read from
23      *  the heap we use a byte array input stream.  So long
24      *  as we continue to read further into the heap, we can
25      *  continue to use the same stream, but we need to
26      *  recreate the stream whenever we skip backwards.
27      */
28     private int heapOffset = 0;
29     /** A stream used to read the heap data */
30     private BufferedDataInputStream bstr;
31
32     /** Create a heap of a given size. */
33     FitsHeap(int size) {
34         heap = new byte[size];
35         heapSize = size;
36     }
37
38     /** Read the heap */
39     public void read(ArrayDataInput str) throws FitsException {
40
41         if (str instanceof RandomAccess) {
42             fileOffset = FitsUtil.findOffset(str);
43             input = str;
44         }
45
46         if (heap != null) {
47             try {
48                 str.read(heap, 0, heapSize);
49             } catch (IOException e) {
50                 throw new FitsException("Error reading heap:" + e);
51             }
52         }
53
54         bstr = null;
55     }
56
57     /** Write the heap */
58     public void write(ArrayDataOutput str) throws FitsException {
59         try {
60             str.write(heap, 0, heapSize);
61         } catch (IOException e) {
62             throw new FitsException("Error writing heap:" + e);
63         }
64     }
65
66     public boolean rewriteable() {
67         return fileOffset >= 0 && input instanceof ArrayDataOutput && !expanded;
68     }
69
70     /** Attempt to rewrite the heap with the current contents.
71      *  Note that no checking is done to make sure that the
72      *  heap does not extend past its prior boundaries.
73      */
74     public void rewrite() throws IOException, FitsException {
75         if (rewriteable()) {
76             ArrayDataOutput str = (ArrayDataOutput) input;
77             FitsUtil.reposition(str, fileOffset);
78             write(str);
79         } else {
80             throw new FitsException("Invalid attempt to rewrite FitsHeap");
81         }
82
83     }
84
85     public boolean reset() {
86         try {
87             FitsUtil.reposition(input, fileOffset);
88             return true;
89         } catch (Exception e) {
90             return false;
91         }
92     }
93
94     /** Get data from the heap.
95      *  @param offset The offset at which the data begins.
96      *  @param array  The array to be extracted.
97      */
98     public void getData(int offset, Object array) throws FitsException {
99
100         try {
101             // Can we reuse the existing byte stream?
102             if (bstr == null || heapOffset > offset) {
103                 heapOffset = 0;
104                 bstr = new BufferedDataInputStream(
105                         new ByteArrayInputStream(heap));
106             }
107
108             bstr.skipBytes(offset - heapOffset);
109             heapOffset = offset;
110             heapOffset += bstr.readLArray(array);
111
112         } catch (IOException e) {
113             throw new FitsException("Error decoding heap area at offset=" + offset
114                     + ".  Exception: Exception " + e);
115         }
116     }
117
118     /** Check if the Heap can accommodate a given requirement.
119      *  If not expand the heap.
120      */
121     void expandHeap(int need) {
122
123         // Invalidate any existing input stream to the heap.
124         bstr = null;
125
126         if (heapSize + need > heap.length) {
127             expanded = true;
128             int newlen = (heapSize + need) * 2;
129             if (newlen < 16384) {
130                 newlen = 16384;
131             }
132             byte[] newHeap = new byte[newlen];
133             System.arraycopy(heap, 0, newHeap, 0, heapSize);
134             heap = newHeap;
135         }
136     }
137
138     /** Add some data to the heap. */
139     int putData(Object data) throws FitsException {
140
141         long lsize = ArrayFuncs.computeLSize(data);
142         if (lsize > Integer.MAX_VALUE) {
143             throw new FitsException("FITS Heap > 2 G");
144         }
145         int size = (int) lsize;
146         expandHeap(size);
147         ByteArrayOutputStream bo = new ByteArrayOutputStream(size);
148
149         try {
150             BufferedDataOutputStream o = new BufferedDataOutputStream(bo);
151             o.writeArray(data);
152             o.flush();
153             o.close();
154         } catch (IOException e) {
155             throw new FitsException("Unable to write variable column length data");
156         }
157
158         System.arraycopy(bo.toByteArray(), 0, heap, heapSize, size);
159         int oldOffset = heapSize;
160         heapSize += size;
161
162         return oldOffset;
163     }
164
165     /** Return the size of the Heap */
166     public int size() {
167         return heapSize;
168     }
169
170     /** Return the size of the heap using the more bean compatbile format */
171     public long getSize() {
172         return size();
173     }
174
175     /** Get the file offset of the heap */
176     public long getFileOffset() {
177         return fileOffset;
178     }
179 }