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 * @return boolean indicating if there was a match 129 */ 130 public static boolean equalsAt(byte[] subArr, byte[] arr, int fIdx) { 131 boolean bRes = false; 132 int lIdx = subArr.length - 1; 133 int tIdx = fIdx + lIdx; 134 if (tIdx < arr.length) { 135 if (subArr[0] == arr[fIdx]) { 136 while (lIdx > 0 && subArr[lIdx] == arr[tIdx]) { 137 --lIdx; 138 --tIdx; 139 } 140 bRes = (lIdx == 0); 141 } 142 } 143 return bRes; 144 } 145 146 /** 147 * Check if a byte array matches a specified case insensitive sub byte array at a certain index. 148 * @param subArr case sensitive sub byte array to compare against 149 * @param arr byte array to look in 150 * @return boolean indicating if there was a match 151 */ 152 public static boolean equalsAtIgnoreCase(byte[] subArr, byte[] arr, int fIdx) { 153 boolean bRes = false; 154 int lIdx = subArr.length - 1; 155 int tIdx = fIdx + lIdx; 156 if (tIdx < arr.length) { 157 if (CASE_INSENSITIVE[subArr[0] & 255] == CASE_INSENSITIVE[arr[fIdx] & 255]) { 158 while (lIdx > 0 && CASE_INSENSITIVE[subArr[lIdx] & 255] == CASE_INSENSITIVE[arr[tIdx] & 255]) { 159 --lIdx; 160 --tIdx; 161 } 162 bRes = (lIdx == 0); 163 } 164 } 165 return bRes; 166 } 167 168 /** 169 * Find the next case sensitive occurrence of a byte sub array in a larger byte array beginning from a specific index. 170 * @param subArr case sensitive byte sub array to compare against 171 * @param arr byte array to look in 172 * @param fIdx index to starting looking from 173 * @return next occurrence or -1, if none was found 174 */ 175 public static int indexOf(byte[] subArr, byte[] arr, int fIdx) { 176 int csIdx; 177 int caIdx; 178 int idx = -1; 179 int subArrLast = subArr.length - 1; 180 int arrLen = arr.length; 181 int lIdx = fIdx + subArrLast; 182 if (subArrLast > 0) { 183 while (lIdx < arrLen && idx == -1) { 184 if (subArr[0] == arr[fIdx]) { 185 csIdx = subArrLast; 186 caIdx = lIdx; 187 while (csIdx > 0 && subArr[csIdx] == arr[caIdx]) { 188 --csIdx; 189 --caIdx; 190 } 191 if (csIdx == 0) { 192 idx = fIdx; 193 } 194 } 195 ++fIdx; 196 ++lIdx; 197 } 198 } 199 else if (subArrLast == 0) { 200 while (fIdx < arrLen && idx == -1) { 201 if (subArr[0] == arr[fIdx]) { 202 idx = fIdx; 203 } 204 ++fIdx; 205 } 206 } 207 return idx; 208 } 209 210 /** 211 * Find the next case insensitive occurrence of a byte sub array in a larger byte array beginning from a specific index. 212 * @param subArr case insensitive byte sub array to compare against 213 * @param arr byte array to look in 214 * @param fIdx index to starting looking from 215 * @return next occurrence or -1, if none was found 216 */ 217 public static int indexOfIgnoreCase(byte[] subArr, byte[] arr, int fIdx) { 218 int csIdx; 219 int caIdx; 220 int idx = -1; 221 int subArrLast = subArr.length - 1; 222 int arrLen = arr.length; 223 int lIdx = fIdx + subArrLast; 224 if (subArrLast > 0) { 225 while (lIdx < arrLen && idx == -1) { 226 if (CASE_INSENSITIVE[subArr[0]] == CASE_INSENSITIVE[arr[fIdx]]) { 227 csIdx = subArrLast; 228 caIdx = lIdx; 229 while (csIdx > 0 && CASE_INSENSITIVE[subArr[csIdx]] == CASE_INSENSITIVE[arr[caIdx]]) { 230 --csIdx; 231 --caIdx; 232 } 233 if (csIdx == 0) { 234 idx = fIdx; 235 } 236 } 237 ++fIdx; 238 ++lIdx; 239 } 240 } 241 else if (subArrLast == 0) { 242 while (fIdx < arrLen && idx == -1) { 243 if (CASE_INSENSITIVE[subArr[0]] == CASE_INSENSITIVE[arr[fIdx]]) { 244 idx = fIdx; 245 } 246 ++fIdx; 247 } 248 } 249 return idx; 250 } 251 252 /** 253 * Case sensitive split a byte array into a list of byte arrays. 254 * @param arr byte array to split 255 * @param subArr case sensitive byte sub array to split around 256 * @param fIdx from index to start from when splitting 257 * @param tIdx to index to end splitting 258 * @return a list of byte arrays 259 */ 260 public static List<byte[]> split(byte[] arr, byte[] subArr, int fIdx, int tIdx) { 261 List<byte[]> list = new LinkedList<byte[]>(); 262 byte[] tmpArr; 263 int csIdx; 264 int caIdx; 265 int subArrLen = subArr.length; 266 int subArrLast = subArrLen - 1; 267 if (arr.length < tIdx) { 268 tIdx = arr.length; 269 } 270 if (fIdx > tIdx) { 271 throw new IllegalArgumentException("Reverse interval!"); 272 } 273 int lIdx = fIdx + subArrLast; 274 int pIdx = fIdx; 275 if (subArrLast > 0) { 276 while (lIdx < tIdx) { 277 if (subArr[0] == arr[fIdx]) { 278 csIdx = subArrLast; 279 caIdx = lIdx; 280 while (csIdx > 0 && subArr[csIdx] == arr[caIdx]) { 281 --csIdx; 282 --caIdx; 283 } 284 if (csIdx == 0) { 285 tmpArr = new byte[fIdx - pIdx]; 286 System.arraycopy(arr, pIdx, tmpArr, 0, tmpArr.length); 287 list.add(tmpArr); 288 fIdx += subArrLen; 289 lIdx += subArrLen; 290 pIdx = fIdx; 291 } 292 else { 293 ++fIdx; 294 ++lIdx; 295 } 296 } 297 else { 298 ++fIdx; 299 ++lIdx; 300 } 301 } 302 if (pIdx < tIdx) { 303 tmpArr = new byte[tIdx - pIdx]; 304 System.arraycopy(arr, pIdx, tmpArr, 0, tmpArr.length); 305 list.add(tmpArr); 306 } 307 } 308 else { 309 while (fIdx < tIdx) { 310 if (subArr[0] == arr[fIdx]) { 311 tmpArr = new byte[fIdx - pIdx]; 312 System.arraycopy(arr, pIdx, tmpArr, 0, tmpArr.length); 313 list.add(tmpArr); 314 pIdx = ++fIdx; 315 } 316 else { 317 ++fIdx; 318 } 319 } 320 if (pIdx < tIdx) { 321 tmpArr = new byte[tIdx - pIdx]; 322 System.arraycopy(arr, pIdx, tmpArr, 0, tmpArr.length); 323 list.add(tmpArr); 324 } 325 } 326 if (pIdx == fIdx) { 327 list.add(zeroArr); 328 } 329 return list; 330 } 331 332}