summaryrefslogtreecommitdiffstats
blob: 997cc6e11e801a8a6c6f65ddf7c055a3a3bb8e55 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
/*
 * Copyright (c) 2010 Remko Tronçon
 * All rights reserved.
 */
/*
 * Copyright (c) 2010-2015, Isode Limited, London, England.
 * All rights reserved.
 */
package com.isode.stroke.base;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;

/**
 * Maintains an arbitrarily long array of bytes.
 */
public class ByteArray {

    /**
     * Construct a new, empty ByteArray that contains no data.
     */
    public ByteArray() {
    }

    /**
     * Constructs a new ByteArray from the bytes corresponding to encoding
     * the provided String as UTF-8.
     * <p> Note that this should only be used for 'real' Strings (e.g. not
     * for TLS data)
     *  
     * @param s String, must not be null.
     */
    public ByteArray(String s) {
        try {
            appendInternal(s.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException ex) {
            throw new IllegalStateException("JVM has no 'UTF-8' encoding");
        }
    }

    /**
     * Constructs a new ByteArray containing the bytes in a user-supplied
     * byte[]
     * @param c an array of bytes, which must not be null, but may contain
     * zero elements.
     */
    public ByteArray(byte[] c) {
        append(c);
    }

    /**
     * Constructs a new ByteArray object by performing a deep copy of the 
     * contents of an existing ByteArray
     * @param b another ByteArray; must not be null
     */
    public ByteArray(ByteArray b) {
        append(b);
    }

    /*public ByteArray(char[] c, int n) {
        for (int i = 0; i < n; i++) {
            append(c[i]);
        }
    }*/

    /**
     * Returns a byte[] containing the contents of this object.
     * @return array copy of internal data, will never be null, but may
     * contain zero elements.
     */
    public synchronized byte[] getData() {
        if (dataCopy_ == null) {
            if (data_.size() == 1) {
                dataCopy_ = data_.get(0);
            } else {
                dataCopy_ = new byte[getSize()];
                int pos = 0;
                for (byte[] chunk : data_) {
                    System.arraycopy(chunk, 0, dataCopy_, pos, chunk.length);
                    pos += chunk.length;
                }
            }
        }
        return dataCopy_;
    }

    /**
     * Returns the number of bytes in this ByteArray
     * @return number of bytes
     */
    public int getSize() {
        return dataSize_;
    }

    /**
     * Determines whether the ByteArray is empty
     * @return <em>true</em> if there are no elements in the ByteArray,
     * <em>false</em> otherwise.
     */
    public boolean isEmpty() {
        return getSize() == 0;
    }

    /*public void resize(size_t size) {
    return data_.resize(size);
    }*/
    /** 
     * Creates a new ByteArray object containing all 
     * the elements from two existing ByteArrays (immutable add).
     * 
     * @param a an existing ByteArray. Must not be null, but may be empty.
     * @param b an existing ByteArray. Must not be null, but may be empty.
     * @return a new ByteArray containing all the elements of <em>a</em>
     * followed by all the elements of <em>b</em>.
     */  
    public static ByteArray plus(ByteArray a, ByteArray b) {
        ByteArray x = new ByteArray(a);
        x.append(b);
        return x;
    }

    /* * Immutable add */
    /*public ByteArray plus(ByteArray a, char b) {
        ByteArray x = new ByteArray(a.getData());
        x.append(b);
        return x;
    }*/
    
    /**
     * Updates the ByteArray by adding all the elements
     * of another ByteArray to the end of the array (mutable add).
     * @param b an existing ByteArray. Must not be null, but may be empty
     * @return a reference to the updated object 
     */
    public synchronized ByteArray append(ByteArray b) {
        dataCopy_ = null; /* Invalidate cache */
        data_.addAll(b.data_);
        dataSize_ += b.getSize();
        return this;
    }

    /** 
     * Updates the ByteArray by adding all the bytes
     * in a byte[] to the end of the array (mutable add).  
     * 
     * @param b an array of bytes. Must not be null, but may contain zero
     * elements.
     * 
     * @return a reference to the updated object
     */
    public ByteArray append(byte[] b) {
        return append(b, b.length);
    }

    /** Mutable add */
    public ByteArray append(byte[] b, int len) {
        return appendInternal(Arrays.copyOf(b, len));
    }

    /* Does not copy data - all calls must provide a copy as necessary */
    private synchronized ByteArray appendInternal(byte[] b) {
        dataCopy_ = null; /* Invalidate cache */
        data_.add(b);
        dataSize_ += b.length;
        return this;
    }

    /** 
     * Updates the ByteArray by adding a single byte
     * value to the end of the array (mutable add).
     * @param b a single byte
     * @return a reference to the updated object
     */
    public ByteArray append(byte b) {
        return appendInternal(new byte[]{b});
    }

    /**
     * Updates the ByteArray by adding all the bytes
     * obtained by UTF-8 encoding the provided String to the end of the array (mutable add).
     * @param s a String that must not be null.
     * @return a reference to the updated object.
     */ 
    public ByteArray append(String s) {
        try {
            return appendInternal(s.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException ex) {
            throw new IllegalStateException("JVM has no 'UTF-8' encoding");
        }
    }

    @Override
    public synchronized int hashCode() {
        return Arrays.hashCode(getData());
    }

    @Override
    public boolean equals(Object other) {
        return other instanceof ByteArray && Arrays.equals(getData(), ((ByteArray)other).getData());
    }

    /*public char charAt(int i) {
        return data_.charAt(i);
    }*/

    /*public const_iterator begin() const {
    return data_.begin();
    }

    public const_iterator end() const {
    return data_.end();
    }*/
    /**
     * Returns a String representation of this object by attempting to 
     * treat its component bytes as a UTF-8 String.  If the ByteArray is empty,
     * an empty String ("") will be returned. If the bytes inside the object
     * are not valid UTF-8, then the resultant String may not be very
     * user-friendly.
     * 
     * @return a String representation of the bytes inside this object
     */
    @Override
    public String toString() {
        try {
            return new String(getData(), "UTF-8");
        } catch (UnsupportedEncodingException ex) {
            throw new IllegalStateException("JVM has no 'UTF-8' encoding");
        }
    }

    /**
     * Appends the contents of a file to the end of an existing ByteStream
     * object (mutable add).
     * 
     * @param file the name of the file to be read. If the file cannot be
     * opened for any reason, or if an error occurs during an attempt to
     * read it, then the contents of the ByteStream are left
     * unchanged (i.e. no exception is thrown).
     */
    public void readFromFile(String file) {
        FileInputStream fis = null;
        ByteArrayOutputStream bos = null;
        try {
            fis = new FileInputStream(file);
            bos = new ByteArrayOutputStream();
            while (fis.available() != 0) {
                bos.write(fis.read());
            }
            byte[] bytes = bos.toByteArray();
            appendInternal(bytes);
        }
        catch (FileNotFoundException e) {
            // Leave things as they were
            return;
        }
        catch (IOException e) {
            // Leave things as they were
            return;
        } finally {
            try {
                if(bos != null) bos.close();
            }
            catch (IOException e) {
                // Needs a catch clause
            }
            try {
                if(fis != null) fis.close();
            }
            catch (IOException e) {
                // Needs a catch clause
            }
        }
    }

    /**
     * Clears the contents of this ByteArray, leaving it with zero elements.
     */
    public synchronized void clear() {
        data_.clear();
        dataSize_ = 0;
        dataCopy_ = null;
    }
    
    private final ArrayList<byte[]> data_ = new ArrayList<byte[]>(1);
    private int dataSize_ = 0;
    private byte[] dataCopy_ = null;
}