/*
 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
 * 
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * The contents of this file are subject to the terms of either the Universal Permissive License
 * v 1.0 as shown at http://oss.oracle.com/licenses/upl
 *
 * or the following license:
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
 * and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other materials provided with
 * the distribution.
 * 
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
 * endorse or promote products derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package examples;

/**
 * A simple piece of code that generates a bunch of duplicated strings and then goes into sleep for
 * long enough for the user to take a heap dump. The resulting dump is stored alongside the test
 * source files and is analyzed in org.openjdk.jmc.joverflow.stats.StringDupTest.
 * <p>
 * IMPORTANT: a heap dump for this class should be generated by the JVM running in 32-bit mode.
 * Numbers in the test are based on pointer size etc. in this mode, where we know their exact values
 * and don't have to care about different object header size on different VMs, compressed
 * references, etc.
 */
@SuppressWarnings("unused")
public class DuplicateStrings {

	public static final int N_UNIQ_STRINGS = 4000;
	public static final int N_SAME_STRINGS_0 = 2000;
	public static final int N_SAME_STRINGS_12 = 2700;
	public static final int N_SAME_STRINGS_3 = 800;
	public static final int N_NONDUP_STRINGS_3 = 100;
	public static final int N_SAME_STRINGS_4 = 800;

	// A prefix for each string, contains 50 characters
	public static final String PREFIX = "12345678901234567890123456789012345678901234567890";

	public String TOP_DUP_STRING_0, TOP_DUP_STRING_1, TOP_DUP_STRING_2, TOP_DUP_STRING_3, TOP_DUP_STRING_4;

	// Each string is unique, contains a prefix plus 8 additional characters
	private final String[] uniqueStrings;

	// This array contains N_SAME_STRINGS_0 different String instances,
	// each with its own backing char[] array. They all have the same
	// value equal to uniqueStrings[0] == TOP_DUP_STRINGS_0.
	private final String[] dupStrings0;

	// This array contains N_SAME_STRINGS_12 different String instances,
	// each with its own backing char[] array. 2/3 of them have the same value
	// equal to uniqueStrings[1], another 1/3 - to uniqueStrings[2]
	// (TOP_DUP_STRING_1 and TOP_DUP_STRING_2)
	private final String[] dupStrings12;

	// This array contains N_SAME_STRINGS_3 different String instances,
	// each with its own backing char[] array, all with the same value
	// equal to TOP_DUP_STRING_3. In addition, it contains N_NONDUP_STRINGS_3
	// non-dup strings, in the range "*#?0".."*#?99"
	private final String[] dupStrings3;

	// This array contains N_SAME_STRINGS_4 different String instances.
	// They all have the same value TOP_DUP_STRINGS_4. However, only half
	// of them have their own backing char[] arrays, whereas another half
	// point at the same char[] array of teh original TOP_DUP_STRING_4.
	private final String[] dupStrings4;

	public static void main(String args[]) {
		DuplicateStrings d = new DuplicateStrings();

		ExampleUtils.printPidAndSleep("duplicate-strings.hprof");
	}

	/** Made public to be used in the same test that analyzes the heap dump */
	public DuplicateStrings() {
		uniqueStrings = new String[N_UNIQ_STRINGS];
		for (int i = 0; i < N_UNIQ_STRINGS; i++) {
			uniqueStrings[i] = PREFIX + String.format("%8d", i);
		}

		dupStrings0 = new String[N_SAME_STRINGS_0];
		TOP_DUP_STRING_0 = uniqueStrings[0];
		for (int i = 0; i < N_SAME_STRINGS_0; i++) {
			dupStrings0[i] = duplicateStringDeep(TOP_DUP_STRING_0);
		}

		dupStrings12 = new String[N_SAME_STRINGS_12];
		TOP_DUP_STRING_1 = uniqueStrings[1];
		TOP_DUP_STRING_2 = uniqueStrings[2];
		for (int i = 0; i < N_SAME_STRINGS_12; i++) {
			dupStrings12[i++] = duplicateStringDeep(TOP_DUP_STRING_1);
			dupStrings12[i++] = duplicateStringDeep(TOP_DUP_STRING_1);
			dupStrings12[i] = duplicateStringDeep(TOP_DUP_STRING_2);
		}

		dupStrings3 = new String[N_SAME_STRINGS_3 + N_NONDUP_STRINGS_3];
		TOP_DUP_STRING_3 = uniqueStrings[3];
		for (int i = 0; i < N_SAME_STRINGS_3; i++) {
			dupStrings3[i] = duplicateStringDeep(TOP_DUP_STRING_3);
		}
		int j = 0;
		for (int i = N_SAME_STRINGS_3; i < N_SAME_STRINGS_3 + N_NONDUP_STRINGS_3; i++) {
			dupStrings3[i] = "*#?" + Integer.toString(j++);
		}

		dupStrings4 = new String[N_SAME_STRINGS_4];
		TOP_DUP_STRING_4 = duplicateStringShallow(uniqueStrings[4]);
		// Use an isolated copy of TOP_DUP_STRING_4 for generating strings in the array.
		// This is done to avoid possible problems with calculating the proper number
		// of backing char arrays for dupStrings4. When a backing char array is shared
		// between Strings in dupStrings4 and TOP_DUP_STRING_4, the number of backing
		// char arrays for dupStrings4 will be 401 or 400 depending on which data struct
		// the heap scanner scans first - dupStrings4 or TOP_DUP_STRING_4.
		String topDupString4 = duplicateStringDeep(TOP_DUP_STRING_4);
		for (int i = 0; i < N_SAME_STRINGS_4; i++) {
			dupStrings4[i++] = duplicateStringDeep(topDupString4);
			dupStrings4[i] = duplicateStringShallow(topDupString4);
		}
	}

	private static String duplicateStringDeep(String s) {
		char chars[] = new char[s.length()];
		s.getChars(0, s.length(), chars, 0);
		return new String(chars);
	}

	private static String duplicateStringShallow(String s) {
		return new String(s);
	}
}
