/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 org.apache.jackrabbit.core.data; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; /** * An input stream that returns pseudo-random bytes. */ public class RandomInputStream extends InputStream { private static final long MUL = 0x5DEECE66DL; private static final long ADD = 0xBL; private static final long MASK = (1L << 48) - 1; private static final int DEFAULT_MAX_READ_BLOCK_SIZE = 15; private final long initialSeed; private final long len; private long markedState; private long pos; private long markedPos; private long state; private int maxReadBlockSize; public String toString() { return "new RandomInputStream(" + initialSeed + ", " + len + ")"; } public RandomInputStream(long seed, long len) { this(seed, len, DEFAULT_MAX_READ_BLOCK_SIZE); } public static void compareStreams(InputStream a, InputStream b) throws IOException { a = new BufferedInputStream(a); b = new BufferedInputStream(b); long pos = 0; while (true) { int x = a.read(); int y = b.read(); if (x == -1 || y == -1) { if (x == y) { break; } } if (x != y) { throw new IOException("Incorrect byte at position " + pos + ": x=" + x + " y=" + y); } } } public RandomInputStream(long seed, long len, int maxReadBlockSize) { this.initialSeed = seed; this.len = len; this.maxReadBlockSize = maxReadBlockSize; setSeed(seed); reset(); } public long skip(long n) { n = getReadBlock(n); if (n == 0) { return -1; } pos += n; return n; } private int getReadBlock(long n) { if (n > (len - pos)) { n = (len - pos); } if (n > maxReadBlockSize) { n = maxReadBlockSize; } else if (n < 0) { n = 0; } return (int) n; } public int read(byte[] b, int off, int len) { if (pos >= this.len) { return -1; } len = getReadBlock(len); if (len == 0) { return -1; } for (int i = 0; i < len; i++) { b[off + i] = (byte) (next() & 255); } pos += len; return len; } public int read(byte[] b) { return read(b, 0, b.length); } public void close() { pos = len; } private void setSeed(long seed) { markedState = (seed ^ MUL) & MASK; } private int next() { state = (state * MUL + ADD) & MASK; return (int) (state >>> (48 - 32)); } public void reset() { pos = markedPos; state = markedState; } public int read() { if (pos >= len) { return -1; } pos++; return next() & 255; } public boolean markSupported() { return true; } public void mark(int readlimit) { markedPos = pos; markedState = state; } }