/**
* Copyright 2009-2013 the original author or authors.
*
* 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 net.javacrumbs.demos.parallel;
import org.apache.commons.codec.digest.DigestUtils;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.util.Random;
import java.util.Spliterator;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import static net.javacrumbs.common.Utils.measure;
public class Hashes2 {
private static final Random random = new Random(3456789L);
public static void main(String[] args) {
LongAdder adder = new LongAdder();
ForkJoinPool fjp = new ForkJoinPool(3);
measure(() ->
spliteratorStream().peek(s -> adder.increment())
.filter(s -> md5(s).startsWith("000000")).findAny().ifPresent(s -> System.out.println(new String(s) + " " + md5(s)))
);
System.out.println(adder);
}
private static Stream<char[]> spliteratorStream() {
return StreamSupport.stream(new RandomStringsSpliterator(20), false);
}
private static String md5(char[] s) {
return DigestUtils.md5Hex(charsToBytesUTFNIO(s));
}
public static byte[] charsToBytesUTFNIO(char[] buffer) {
byte[] b = new byte[buffer.length << 1];
CharBuffer cBuffer = ByteBuffer.wrap(b).asCharBuffer();
for (int i = 0; i < buffer.length; i++)
cBuffer.put(buffer[i]);
return b;
}
private static char[] randomString(char[] buffer) {
return random(0, 0, true, true, random, buffer);
}
static final class RandomStringsSpliterator implements Spliterator {
long index;
final long fence;
final int length;
RandomStringsSpliterator(int length) {
this(0, Long.MAX_VALUE, length);
}
RandomStringsSpliterator(long index, long fence,
int length) {
this.index = index;
this.fence = fence;
this.length = length;
}
public RandomStringsSpliterator trySplit() {
long i = index, m = (i + fence) >>> 1;
return (m <= i) ? null :
new RandomStringsSpliterator(i, index = m, length);
}
public long estimateSize() {
return fence - index;
}
public int characteristics() {
return (Spliterator.NONNULL | Spliterator.IMMUTABLE);
}
public boolean tryAdvance(Consumer consumer) {
if (consumer == null) throw new NullPointerException();
long i = index, f = fence;
char[] buffer = new char[length];
if (i < f) {
consumer.accept(randomString(buffer));
index = i + 1;
return true;
}
return false;
}
public void forEachRemaining(Consumer consumer) {
if (consumer == null) throw new NullPointerException();
long i = index, f = fence;
if (i < f) {
index = f;
char[] buffer = new char[length];
do {
consumer.accept(randomString(buffer));
} while (++i < f);
}
}
}
/**
* <p>Creates a random string based on a variety of options, using
* supplied source of randomness.</p>
* <p>
* <p>If start and end are both {@code 0}, start and end are set
* to {@code ' '} and {@code 'z'}, the ASCII printable
* characters, will be used, unless letters and numbers are both
* {@code false}, in which case, start and end are set to
* {@code 0} and {@code Integer.MAX_VALUE}.
* <p>
* <p>If set is not {@code null}, characters between start and
* end are chosen.</p>
* <p>
* <p>This method accepts a user-supplied {@link java.util.Random}
* instance to use as a source of randomness. By seeding a single
* {@link java.util.Random} instance with a fixed seed and using it for each call,
* the same random sequence of strings can be generated repeatedly
* and predictably.</p>
*
* @param start the position in set of chars to start at
* @param end the position in set of chars to end before
* @param letters only allow letters?
* @param numbers only allow numbers?
* If {@code null}, then it will use the set of all chars.
* @param random a source of randomness.
* @return the random string
* @throws ArrayIndexOutOfBoundsException if there are not
* {@code (end - start) + 1} characters in the set array.
* @throws IllegalArgumentException if {@code count} < 0.
* @since 2.0
*/
public static char[] random(int start, int end, boolean letters, boolean numbers,
Random random, char[] buffer) {
int count = buffer.length;
if (start == 0 && end == 0) {
end = 'z' + 1;
start = ' ';
if (!letters && !numbers) {
start = 0;
end = Integer.MAX_VALUE;
}
}
int gap = end - start;
while (count-- != 0) {
char ch = (char) (random.nextInt(gap) + start);
if (letters && Character.isLetter(ch)
|| numbers && Character.isDigit(ch)
|| !letters && !numbers) {
if (ch >= 56320 && ch <= 57343) {
if (count == 0) {
count++;
} else {
// low surrogate, insert high surrogate after putting it in
buffer[count] = ch;
count--;
buffer[count] = (char) (55296 + random.nextInt(128));
}
} else if (ch >= 55296 && ch <= 56191) {
if (count == 0) {
count++;
} else {
// high surrogate, insert low surrogate before putting it in
buffer[count] = (char) (56320 + random.nextInt(128));
count--;
buffer[count] = ch;
}
} else if (ch >= 56192 && ch <= 56319) {
// private high surrogate, no effing clue, so skip it
count++;
} else {
buffer[count] = ch;
}
} else {
count++;
}
}
return buffer;
}
}