• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2010 Google Inc. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.typography.font.sfntly.data;
18 
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.io.OutputStream;
22 
23 /**
24  * An abstraction to a contiguous array of bytes.
25  *
26  * @param <T> the concrete sub-class of ByteArray
27  *
28  * @author Stuart Gill
29  */
30 abstract class ByteArray<T extends ByteArray<T>> {
31   private static final int COPY_BUFFER_SIZE = 8192;
32 
33   @SuppressWarnings("unused")
34   private boolean bound;
35 
36   private int storageLength;
37   private int filledLength;
38   private boolean growable;
39 
40   /**
41    * Constructor.
42    *
43    * @param filledLength the length that is "filled" and readable counting from the offset
44    * @param storageLength the maximum storage size of the underlying data
45    * @param growable is the storage growable - storageLength is the maximum growable size
46    */
ByteArray(int filledLength, int storageLength, boolean growable)47   protected ByteArray(int filledLength, int storageLength, boolean growable) {
48     this.storageLength = storageLength;
49     this.setFilledLength(filledLength);
50     this.growable = growable;
51   }
52 
53   /**
54    * Constructor.
55    *
56    * @param filledLength the length that is "filled" and readable counting from the offset
57    * @param storageLength the maximum storage size of the underlying data
58    */
ByteArray(int filledLength, int storageLength)59   protected ByteArray(int filledLength, int storageLength) {
60     this(filledLength, storageLength, false);
61   }
62 
63   /**
64    * Gets the byte from the given index.
65    *
66    * @param index the index into the byte array
67    * @return the byte or -1 if reading beyond the bounds of the data
68    */
get(int index)69   public int get(int index) {
70     if (index < 0 || index >= this.filledLength) {
71       return -1;
72     }
73     return this.internalGet(index) & 0xff;
74   }
75 
76   /**
77    * Gets the bytes from the given index and fill the buffer with them.
78    * As many bytes as will fit into the buffer are read unless that
79    * would go past the end of the array.
80    *
81    * @param index the index into the byte array
82    * @param b the buffer to put the bytes read into
83    * @return the number of bytes read from the buffer
84    */
get(int index, byte[] b)85   public int get(int index, byte[] b) {
86     return this.get(index, b, 0, b.length);
87   }
88 
89   /**
90    * Gets the bytes from the given index and fill the buffer with them starting at the offset given.
91    * As many bytes as the specified length are read unless that would go past the end of the array.
92    *
93    * @param index the index into the byte array
94    * @param b the buffer to put the bytes read into
95    * @param offset the location in the buffer to start putting the bytes
96    * @param length the number of bytes to put into the buffer
97    * @return the number of bytes read from the buffer
98    */
get(int index, byte[] b, int offset, int length)99   public int get(int index, byte[] b, int offset, int length) {
100     if (index < 0 || index >= this.filledLength) {
101       return -1;
102     }
103     int actualLength = Math.min(length, this.filledLength - index);
104     return this.internalGet(index, b, offset, actualLength);
105   }
106 
107   /**
108    * Gets the current filled and readable length of the array.
109    *
110    * @return the current length
111    */
length()112   public int length() {
113     return this.filledLength;
114   }
115 
116   /**
117    * Gets the maximum size of the array. This is the maximum number of bytes that
118    * the array can hold and all of it may not be filled with data or even fully
119    * allocated yet.
120    *
121    * @return the size of this array
122    */
size()123   public int size() {
124     return this.storageLength;
125   }
126 
127   /**
128    * Determines whether or not this array is growable or of fixed size.
129    *
130    * @return true if the array is growable; false otherwise
131    */
growable()132   public final boolean growable() {
133     return this.growable;
134   }
135 
setFilledLength(int filledLength)136   public int setFilledLength(int filledLength) {
137     this.filledLength = Math.min(filledLength, this.storageLength);
138     return this.filledLength;
139   }
140 
141   /**
142    * Puts the specified byte into the array at the given index unless that would
143    * be beyond the length of the array and it
144    * isn't growable.
145    *
146    * @param index the index into the byte array
147    * @param b the byte to put into the array
148    * @throws IndexOutOfBoundsException if attempt to write outside the bounds of the data
149    */
put(int index, byte b)150   public void put(int index, byte b) {
151     if (index < 0 || index >= this.size()) {
152       throw new IndexOutOfBoundsException("Attempt to write outside the bounds of the data.");
153     }
154     this.internalPut(index, b);
155     this.filledLength = Math.max(this.filledLength, index + 1);
156   }
157 
158   /**
159    * Puts the specified bytes into the array at the given index.
160    * The entire buffer is put into the array unless that would
161    * extend beyond the length and the array isn't growable.
162    * @param index the index into the byte array
163    * @param b the bytes to put into the array
164    * @return the number of bytes actually written
165    * @throws IndexOutOfBoundsException if the index for writing is outside the bounds of the data
166    */
put(int index, byte[] b)167   public int put(int index, byte[] b) {
168     return this.put(index, b, 0, b.length);
169   }
170 
171   /**
172    * Puts the specified bytes into the array at the given index. All of the bytes
173    * specified are put into the array unless that would extend beyond the length
174    * and the array isn't growable. The bytes to be put into the array are those
175    * in the buffer from the given offset and for the given length.
176    *
177    * @param index the index into the ByteArray
178    * @param b the bytes to put into the array
179    * @param offset the offset in the bytes to start copying from
180    * @param length the number of bytes to copy into the array
181    * @return the number of bytes actually written
182    * @throws IndexOutOfBoundsException if the index for writing is outside the bounds of the data
183    */
put(int index, byte[] b, int offset, int length)184   public int put(int index, byte[] b, int offset, int length) {
185     if (index < 0 || index >= this.size()) {
186       throw new IndexOutOfBoundsException("Attempt to write outside the bounds of the data.");
187     }
188     int actualLength = Math.min(length, this.size() - index);
189     int bytesWritten = this.internalPut(index, b, offset, actualLength);
190     this.filledLength = Math.max(this.filledLength, index + bytesWritten);
191     return bytesWritten;
192   }
193 
194   /**
195    * Fully copies this ByteArray to another ByteArray to the extent that the
196    * destination array has storage for the data copied.
197    *
198    * @param array the destination
199    * @return the number of bytes copied
200    */
copyTo(ByteArray<? extends ByteArray<?>> array)201   public int copyTo(ByteArray<? extends ByteArray<?>> array) {
202     return copyTo(array, 0, this.length());
203   }
204 
205   /**
206    * Copies a segment of this ByteArray to another ByteArray.
207    *
208    * @param array the destination
209    * @param offset the offset in this ByteArray to start copying from
210    * @param length the maximum length in bytes to copy
211    * @return the number of bytes copied
212    */
213   public int
copyTo(ByteArray<? extends ByteArray<?>> array, int offset, int length)214   copyTo(ByteArray<? extends ByteArray<?>> array, int offset, int length) {
215     return this.copyTo(0, array, offset, length);
216   }
217 
218   /**
219    * Copies this ByteArray to another ByteArray.
220    *
221    * @param dstOffset the offset in the destination array to start copying to
222    * @param array the destination
223    * @param srcOffset the offset in this ByteArray to start copying from
224    * @param length the maximum length in bytes to copy
225    * @return the number of bytes copied
226    */
copyTo( int dstOffset, ByteArray<? extends ByteArray<?>> array, int srcOffset, int length)227   public int copyTo(
228       int dstOffset, ByteArray<? extends ByteArray<?>> array, int srcOffset, int length) {
229     byte[] b = new byte[COPY_BUFFER_SIZE];
230     int bytesRead = 0;
231     int index = 0;
232     int bufferLength = Math.min(b.length, length);
233     while ((bytesRead = this.get(index + srcOffset, b, 0, bufferLength)) > 0) {
234       int bytesWritten = array.put(index + dstOffset, b, 0, bytesRead);
235       index += bytesRead;
236       length -= bytesRead;
237       bufferLength = Math.min(b.length, length);
238     }
239     return index;
240   }
241 
242   /**
243    * Copies this ByteArray to an OutputStream.
244    * @param os the destination
245    * @return the number of bytes copied
246    * @throws IOException
247    */
copyTo(OutputStream os)248   public int copyTo(OutputStream os) throws IOException {
249     return this.copyTo(os, 0, this.length());
250   }
251 
252   /**
253    * Copies this ByteArray to an OutputStream.
254    *
255    * @param os the destination
256    * @param offset
257    * @param length
258    * @return the number of bytes copied
259    * @throws IOException
260    */
copyTo(OutputStream os, int offset, int length)261   public int copyTo(OutputStream os, int offset, int length) throws IOException {
262     byte[] b = new byte[COPY_BUFFER_SIZE];
263     int bytesRead = 0;
264     int index = 0;
265     int bufferLength = Math.min(b.length, length);
266     while ((bytesRead = this.get(index + offset, b, 0, bufferLength)) > 0) {
267       os.write(b, 0, bytesRead);
268       index += bytesRead;
269       bufferLength = Math.min(b.length, length - index);
270     }
271     return index;
272   }
273 
274   /**
275    * Copies from the InputStream into this ByteArray.
276    *
277    * @param is the source
278    * @param length the number of bytes to copy
279    * @throws IOException
280    */
copyFrom(InputStream is, int length)281   public void copyFrom(InputStream is, int length) throws IOException {
282     byte[] b = new byte[COPY_BUFFER_SIZE];
283     int bytesRead = 0;
284     int index = 0;
285     int bufferLength = Math.min(b.length, length);
286     while ((bytesRead = is.read(b, 0, bufferLength)) > 0) {
287       if (this.put(index, b, 0, bytesRead) != bytesRead) {
288         throw new IOException("Error writing bytes.");
289       }
290       index += bytesRead;
291       length -= bytesRead;
292       bufferLength = Math.min(b.length, length);
293     }
294   }
295 
296   /**
297    * Copies everything from the InputStream into this ByteArray.
298    *
299    * @param is the source
300    * @throws IOException
301    */
copyFrom(InputStream is)302   public void copyFrom(InputStream is) throws IOException {
303     byte[] b = new byte[COPY_BUFFER_SIZE];
304     int bytesRead = 0;
305     int index = 0;
306     int bufferLength = b.length;
307     while ((bytesRead = is.read(b, 0, bufferLength)) > 0) {
308       if (this.put(index, b, 0, bytesRead) != bytesRead) {
309         throw new IOException("Error writing bytes.");
310       }
311       index += bytesRead;
312     }
313   }
314 
315   // ********************************************************************
316   // Internal Subclass API
317   // ********************************************************************
318 
319   /**
320    * Stores the byte at the index given.
321    *
322    * @param index the location to store at
323    * @param b the byte to store
324    */
internalPut(int index, byte b)325   protected abstract void internalPut(int index, byte b);
326 
327   /**
328    * Stores the array of bytes at the given index.
329    *
330    * @param index the location to store at
331    * @param b the bytes to store
332    * @param offset the offset to start from in the byte array
333    * @param length the length of the byte array to store from the offset
334    * @return the number of bytes actually stored
335    */
internalPut(int index, byte[] b, int offset, int length)336   protected abstract int internalPut(int index, byte[] b, int offset, int length);
337 
338   /**
339    * Gets the byte at the index given.
340    *
341    * @param index the location to get from
342    * @return the byte stored at the index
343    */
internalGet(int index)344   protected abstract int internalGet(int index);
345 
346   /**
347    * Gets the bytes at the index given of the given length.
348    *
349    * @param index the location to start getting from
350    * @param b the array to put the bytes into
351    * @param offset the offset in the array to put the bytes into
352    * @param length the length of bytes to read
353    * @return the number of bytes actually ready
354    */
internalGet(int index, byte[] b, int offset, int length)355   protected abstract int internalGet(int index, byte[] b, int offset, int length);
356 
357   /**
358    * Close this instance of the ByteArray.
359    */
close()360   public abstract void close();
361 
362   /**
363    * Returns a string representation of the ByteArray.
364    *
365    * @param length the number of bytes of the ByteArray to include in the String
366    * @return a string representation of the ByteArray
367    */
toString(int offset, int length)368   public String toString(int offset, int length) {
369     if (length == -1) {
370       length = this.length();
371     }
372     length = Math.min(length, this.length());
373     StringBuilder sb = new StringBuilder();
374 
375     sb.append("[l=" + this.filledLength + ", s=" + this.size() + "]");
376     if (length > 0) {
377       sb.append("\n");
378     }
379     for (int i = 0; i < length; i++) {
380       int r = this.get(i + offset);
381       if (r < 0x10) {
382         sb.append("0");
383       }
384       sb.append(Integer.toHexString(r));
385       sb.append(" ");
386       if (i > 0 && ((i + 1) % 16) == 0) {
387         sb.append("\n");
388       }
389     }
390     return sb.toString();
391   }
392 
393   @Override
toString()394   public String toString() {
395     return this.toString(0, 0);
396   }
397 }