001/**
002 * Java Web Archive Toolkit - Software to read and validate ARC, WARC
003 * and GZip files. (http://jwat.org/)
004 * Copyright 2011-2012 Netarkivet.dk (http://netarkivet.dk/)
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.jwat.common;
019
020import java.util.LinkedList;
021import java.util.List;
022
023/**
024 * Common byte array helper methods.
025 *
026 * @author nicl
027 */
028public class ArrayUtils {
029
030    /** Case sensitive array where [i] == i. */
031    protected static final byte[] CASE_SENSITIVE = new byte[256];
032    /** Case insensitive array where [i] == toLowerCase(i). */
033    protected static final byte[] CASE_INSENSITIVE = new byte[256];
034
035    /** Array where the character equal to the index is 1 only for whitespace characters. **/
036    public static final byte[] SKIP_WHITESPACE = new byte[256];
037    /** Array where the character equal to the index is 1 only for non whitespace characters. **/
038    public static final byte[] SKIP_NONWHITESPACE = new byte[256];
039
040    /** Zero length byte array. */
041    protected static final byte[] zeroArr = new byte[0];
042
043    /**
044     * Initialize static arrays.
045     */
046    static {
047        for (int i=0; i<256; ++i) {
048            CASE_SENSITIVE[i] = (byte)i;
049            CASE_INSENSITIVE[i] = (byte)Character.toLowerCase(i);
050            SKIP_WHITESPACE[i] = 0;
051            SKIP_NONWHITESPACE[i] = 1;
052        }
053        SKIP_WHITESPACE[' '] = 1;
054        SKIP_WHITESPACE['\t'] = 1;
055        SKIP_WHITESPACE['\r'] = 1;
056        SKIP_WHITESPACE['\n'] = 1;
057        SKIP_NONWHITESPACE[' '] = 0;
058        SKIP_NONWHITESPACE['\t'] = 0;
059        SKIP_NONWHITESPACE['\r'] = 0;
060        SKIP_NONWHITESPACE['\n'] = 0;
061    }
062
063    /**
064     * Purely for unit testing.
065     */
066    protected ArrayUtils() {
067    }
068
069    /**
070     * Move index forward according to the values in the byte array and the skip byte array which decides which byte values must be skipped.
071     * @param skip byte array deciding which byte values must be skipped
072     * @param arr byte array to skip in
073     * @param fIdx byte array index
074     * @return updated index depending of how many bytes where skipped
075     */
076    public static int skip(byte[] skip, byte[] arr, int fIdx) {
077        int arrLen = arr.length;
078        while (fIdx < arrLen && skip[arr[fIdx] & 255] == 1) {
079            ++fIdx;
080        }
081        return fIdx;
082    }
083
084    /**
085     * Check if a byte array starts with a specified case sensitive sub byte array.
086     * @param subArr case sensitive sub byte array to compare for
087     * @param arr byte array to look in
088     * @return boolean indicating if there was a match
089     */
090    public static boolean startsWith(byte[] subArr, byte[] arr) {
091        boolean bRes = false;
092        int lIdx = subArr.length - 1;
093        if (lIdx < arr.length) {
094            if (subArr[0] == arr[0]) {
095                while (lIdx > 0 && subArr[lIdx] == arr[lIdx]) {
096                    --lIdx;
097                }
098                bRes = (lIdx == 0);
099            }
100        }
101        return bRes;
102    }
103
104    /**
105     * Check if a byte array starts with a specified case insensitive sub byte array.
106     * @param subArr case insensitive sub byte array to compare for
107     * @param arr byte array to look in
108     * @return boolean indicating if there was a match
109     */
110    public static boolean startsWithIgnoreCase(byte[] subArr, byte[] arr) {
111        boolean bRes = false;
112        int lIdx = subArr.length - 1;
113        if (lIdx < arr.length) {
114            if (CASE_INSENSITIVE[subArr[0]] == CASE_INSENSITIVE[arr[0]]) {
115                while (lIdx > 0 && CASE_INSENSITIVE[subArr[lIdx]] == CASE_INSENSITIVE[arr[lIdx]]) {
116                    --lIdx;
117                }
118                bRes = (lIdx == 0);
119            }
120        }
121        return bRes;
122    }
123
124    /**
125     * Check if a byte array matches a specified case sensitive sub byte array at a certain index.
126     * @param subArr case sensitive sub byte array to compare against
127     * @param arr byte array to look in
128     * @param fIdx index to start looking from in <code>arr</code>
129     * @return boolean indicating if there was a match
130     */
131    public static boolean equalsAt(byte[] subArr, byte[] arr, int fIdx) {
132        boolean bRes = false;
133        int lIdx = subArr.length - 1;
134        int tIdx = fIdx + lIdx;
135        if (tIdx < arr.length) {
136            if (subArr[0] == arr[fIdx]) {
137                while (lIdx > 0 && subArr[lIdx] == arr[tIdx]) {
138                    --lIdx;
139                    --tIdx;
140                }
141                bRes = (lIdx == 0);
142            }
143        }
144        return bRes;
145    }
146
147    /**
148     * Check if a byte array matches a specified case insensitive sub byte array at a certain index.
149     * @param subArr case sensitive sub byte array to compare against
150     * @param arr byte array to look in
151     * @param fIdx index to start looking from in <code>arr</code>
152     * @return boolean indicating if there was a match
153     */
154    public static boolean equalsAtIgnoreCase(byte[] subArr, byte[] arr, int fIdx) {
155        boolean bRes = false;
156        int lIdx = subArr.length - 1;
157        int tIdx = fIdx + lIdx;
158        if (tIdx < arr.length) {
159            if (CASE_INSENSITIVE[subArr[0] & 255] == CASE_INSENSITIVE[arr[fIdx] & 255]) {
160                while (lIdx > 0 && CASE_INSENSITIVE[subArr[lIdx] & 255] == CASE_INSENSITIVE[arr[tIdx] & 255]) {
161                    --lIdx;
162                    --tIdx;
163                }
164                bRes = (lIdx == 0);
165            }
166        }
167        return bRes;
168    }
169
170    /**
171     * Find the next case sensitive occurrence of a byte sub array in a larger byte array beginning from a specific index.
172     * @param subArr case sensitive byte sub array to compare against
173     * @param arr byte array to look in
174     * @param fIdx index to starting looking from
175     * @return next occurrence or -1, if none was found
176     */
177    public static int indexOf(byte[] subArr, byte[] arr, int fIdx) {
178        int csIdx;
179        int caIdx;
180        int idx = -1;
181        int subArrLast = subArr.length - 1;
182        int arrLen = arr.length;
183        int lIdx = fIdx + subArrLast;
184        if (subArrLast > 0) {
185            while (lIdx < arrLen && idx == -1) {
186                if (subArr[0] == arr[fIdx]) {
187                    csIdx = subArrLast;
188                    caIdx = lIdx;
189                    while (csIdx > 0 && subArr[csIdx] == arr[caIdx]) {
190                        --csIdx;
191                        --caIdx;
192                    }
193                    if (csIdx == 0) {
194                        idx = fIdx;
195                    }
196                }
197                ++fIdx;
198                ++lIdx;
199            }
200        }
201        else if (subArrLast == 0) {
202            while (fIdx < arrLen && idx == -1) {
203                if (subArr[0] == arr[fIdx]) {
204                    idx = fIdx;
205                }
206                ++fIdx;
207            }
208        }
209        return idx;
210    }
211
212    /**
213     * Find the next case insensitive occurrence of a byte sub array in a larger byte array beginning from a specific index.
214     * @param subArr case insensitive byte sub array to compare against
215     * @param arr byte array to look in
216     * @param fIdx index to starting looking from
217     * @return next occurrence or -1, if none was found
218     */
219    public static int indexOfIgnoreCase(byte[] subArr, byte[] arr, int fIdx) {
220        int csIdx;
221        int caIdx;
222        int idx = -1;
223        int subArrLast = subArr.length - 1;
224        int arrLen = arr.length;
225        int lIdx = fIdx + subArrLast;
226        if (subArrLast > 0) {
227            while (lIdx < arrLen && idx == -1) {
228                if (CASE_INSENSITIVE[subArr[0]] == CASE_INSENSITIVE[arr[fIdx]]) {
229                    csIdx = subArrLast;
230                    caIdx = lIdx;
231                    while (csIdx > 0 && CASE_INSENSITIVE[subArr[csIdx]] == CASE_INSENSITIVE[arr[caIdx]]) {
232                        --csIdx;
233                        --caIdx;
234                    }
235                    if (csIdx == 0) {
236                        idx = fIdx;
237                    }
238                }
239                ++fIdx;
240                ++lIdx;
241            }
242        }
243        else if (subArrLast == 0) {
244            while (fIdx < arrLen && idx == -1) {
245                if (CASE_INSENSITIVE[subArr[0]] == CASE_INSENSITIVE[arr[fIdx]]) {
246                    idx = fIdx;
247                }
248                ++fIdx;
249            }
250        }
251        return idx;
252    }
253
254    /**
255     * Case sensitive split a byte array into a list of byte arrays.
256     * @param arr byte array to split
257     * @param subArr case sensitive byte sub array to split around
258     * @param fIdx from index to start from when splitting
259     * @param tIdx to index to end splitting
260     * @return a list of byte arrays
261     */
262    public static List<byte[]> split(byte[] arr, byte[] subArr, int fIdx, int tIdx) {
263        List<byte[]> list = new LinkedList<byte[]>();
264        byte[] tmpArr;
265        int csIdx;
266        int caIdx;
267        int subArrLen = subArr.length;
268        int subArrLast = subArrLen - 1;
269        if (arr.length < tIdx) {
270            tIdx = arr.length;
271        }
272        if (fIdx > tIdx) {
273            throw new IllegalArgumentException("Reverse interval!");
274        }
275        int lIdx = fIdx + subArrLast;
276        int pIdx = fIdx;
277        if (subArrLast > 0) {
278            while (lIdx < tIdx) {
279                if (subArr[0] == arr[fIdx]) {
280                    csIdx = subArrLast;
281                    caIdx = lIdx;
282                    while (csIdx > 0 && subArr[csIdx] == arr[caIdx]) {
283                        --csIdx;
284                        --caIdx;
285                    }
286                    if (csIdx == 0) {
287                        tmpArr = new byte[fIdx - pIdx];
288                        System.arraycopy(arr, pIdx, tmpArr, 0, tmpArr.length);
289                        list.add(tmpArr);
290                        fIdx += subArrLen;
291                        lIdx += subArrLen;
292                        pIdx = fIdx;
293                    }
294                    else {
295                        ++fIdx;
296                        ++lIdx;
297                    }
298                }
299                else {
300                    ++fIdx;
301                    ++lIdx;
302                }
303            }
304            if (pIdx < tIdx) {
305                tmpArr = new byte[tIdx - pIdx];
306                System.arraycopy(arr, pIdx, tmpArr, 0, tmpArr.length);
307                list.add(tmpArr);
308            }
309        }
310        else {
311            while (fIdx < tIdx) {
312                if (subArr[0] == arr[fIdx]) {
313                    tmpArr = new byte[fIdx - pIdx];
314                    System.arraycopy(arr, pIdx, tmpArr, 0, tmpArr.length);
315                    list.add(tmpArr);
316                    pIdx = ++fIdx;
317                }
318                else {
319                    ++fIdx;
320                }
321            }
322            if (pIdx < tIdx) {
323                tmpArr = new byte[tIdx - pIdx];
324                System.arraycopy(arr, pIdx, tmpArr, 0, tmpArr.length);
325                list.add(tmpArr);
326            }
327        }
328        if (pIdx == fIdx) {
329            list.add(zeroArr);
330        }
331        return list;
332    }
333
334}