// Copyright 2015 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.devtools.build.android.ziputils; import com.google.common.base.Preconditions; import java.nio.ByteBuffer; /** * Helper class for finding byte patterns in a byte buffer, scanning forwards or backwards. * This is used to located the "end of central directory" marker, and in other instances * where zip file elements must be located by scanning for known signatures. * For instance, when it's necessary to scan for zip entries, without * relying on the central directory to provide the exact location, or when scanning for * data descriptor at the end of an entry of unknown size. */ public class ScanUtil { /** * Finds the next previous position of a given byte sequence in a given byte buffer, * starting from the buffer's current position (excluded). * * @param target byte sequence to search for. * @param buffer byte buffer in which to search. * @return position of the last location of the target sequence, at a position * strictly smaller than the current position, or -1 if not found. * @throws IllegalArgumentException if either {@code target} or {@code buffer} is {@code null}. */ static int scanBackwardsTo(byte[] target, ByteBuffer buffer) { Preconditions.checkNotNull(target); Preconditions.checkNotNull(buffer); if (target.length == 0) { return buffer.position() - 1; } int pos = buffer.position() - target.length; if (pos < 0) { return -1; } scan: while (true) { while (pos >= 0 && buffer.get(pos) != target[0]) { pos--; } if (pos < 0) { return -1; } for (int i = 1; i < target.length; i++) { if (buffer.get(pos + i) != target[i]) { pos--; continue scan; } } return pos; } } /** * Finds the next position of a given byte sequence in a given byte buffer, starting from the * buffer's current position (included). * * @param target byte sequence to search for. * @param buffer byte buffer in which to search. * @return position of the first location of the target sequence, or -1 if not found. * @throws IllegalArgumentException if either {@code target} or {@code buffer} is {@code null}. */ static int scanTo(byte[] target, ByteBuffer buffer) { Preconditions.checkNotNull(target); Preconditions.checkNotNull(buffer); if (!buffer.hasRemaining()) { return -1; } int pos = buffer.position(); if (target.length == 0) { return pos; } scan: while (true) { while (pos <= buffer.limit() - target.length && buffer.get(pos) != target[0]) { pos++; } if (pos > buffer.limit() - target.length + 1) { return -1; } for (int i = 1; i < target.length; i++) { if (buffer.get(pos + i) != target[i]) { pos++; continue scan; } } return pos; } } }